Why won't the second pushed tile show? - python

I placed the rectangles over the images. I then bound a click to a call that flipped tiles over by lowering the rectangle below the image. It works for the first call to the function, but when I click another tile, that one won't flip over. The program still registers the second flip because it'll flip everything back over if it's an incorrect match; the only problem is that it won't have the rectangle go under the image.
# ======================================= import statements
import tkinter as tk
import time
import random
import PIL
import PIL.Image as Image
import PIL.ImageTk as ImageTk
# ======================================= class def
class MemoryGame:
def __init__(self):
#initialize window
self.window = tk.Tk()
self.window.title("Sea Life Memory Game")
self.window.minsize(590, 600)
self.window.maxsize(590, 600)
#set main canvas as background
self.canvas = tk.Canvas(self.window, bg="lightblue",
bd=0, highlightthickness=0,
width=590, height=600)
self.canvas.grid(row=0, column=0)
self.canvas.bind("<Button-1>", self.chooseTile)
#establish coordinates for tiles and shuffle image placement
coordinates = [(5,30,105,130), (5,160,105,260), (5,290,105,390), (5,420,105,520), (125,30,225,130), (125,160,225,260), (125,290,225,390), (125,420,225,520), (245,30,345,130), (245,160,345,260), (245,290,345,390), (245,420,345,520), (365,30,465,130), (365,160,465,260), (365,290,465,390), (365,420,465,520), (485,30,585,130), (485,160,585,260), (485,290,585,390), (485,420,585,520)]
imageChoices = ['cropped images/001-turtle.png','cropped images/007-blowfish.png','cropped images/010-jellyfish.png','cropped images/011-starfish.png','cropped images/018-lobster.png','cropped images/028-fish.png','cropped images/033-walrus.png','cropped images/042-goldfish.png','cropped images/045-seal.png','cropped images/046-penguin.png']
random.shuffle(coordinates)
#write title to top of canvas
self.canvas.create_text(295, 15, text="Sea Life Memory Game!",
anchor="center", fill="white",
font="Times 24 bold")
self.selectedTile = None
#initialize counts
coordinateCount = 0
imageCount = 0
self.imageCollection = {}
#for loop to attach images to each rectangle on the canvas
for i in range(len(imageChoices)):
otherDict = {}
x1, y1, x2, y2 = coordinates[coordinateCount]
# if imageCount <= 9:
self.image = ImageTk.PhotoImage(Image.open(imageChoices[imageCount]))
self.image.img = self.image
self.id = self.canvas.create_image(x1, y1, anchor="nw",
image=self.image.img)
self.canvas.create_rectangle(x1, y1, x2, y2, fill="white", outline="white")
coordinateCount += 1
x1, y1, x2, y2 = coordinates[coordinateCount]
self.id = self.canvas.create_image(x1, y1, anchor="nw",
image=self.image.img)
self.canvas.create_rectangle(x1, y1, x2, y2, fill="white", outline="white")
coordinateCount += 1
imageCount += 1
otherDict["faceDown"] = True
self.imageCollection[self.id] = otherDict
#create instructional text
self.canvas.create_text(295, 550, text="Find all the pairs as fast as possible.",
fill="white", font="Times 18", anchor="center")
self.canvas.create_text(295, 570, text="Click on a card to turn it over and find the same matching card.",
fill="white", font="Times 18", anchor="center")
def run(self):
self.window.mainloop()
global list
list = []
def chooseTile(self, event):
# global list
x = event.x
y = event.y
item = self.canvas.find_overlapping(x-5,y-5,x+5,y+5)
list.append(item)
print(len(list))
if len(list) < 2:
self.canvas.tag_lower(list[0][1])
elif len(list) == 2:
self.canvas.tag_lower(list[1][1])
if self.canvas.itemcget(list[0][0], "image") == self.canvas.itemcget(list[1][0], "image"):
list.clear()
else:
time.sleep(1.0)
self.canvas.lower(list[0][0], list[0][1])
self.canvas.lower(list[1][0], list[1][1])
list.clear()
# ======================================= script calls
game = MemoryGame()
game.run()

It is because the update will be performed after chooseTile() returns to tkinter mainloop(). But the images are already reset to lower layer when the function returns, so you cannot see the second selected image.
The simple fix is calling self.canvas.update_idletasks() to force the update to show the second selected image before time.sleep(1.0):
def chooseTile(self, event):
# global list
x = event.x
y = event.y
item = self.canvas.find_overlapping(x-5,y-5,x+5,y+5)
list.append(item)
print(len(list))
if len(list) < 2:
self.canvas.tag_lower(list[0][1])
elif len(list) == 2:
self.canvas.tag_lower(list[1][1])
if self.canvas.itemcget(list[0][0], "image") == self.canvas.itemcget(list[1][0], "image"):
list.clear()
else:
# force the canvas to show the second selected image
self.canvas.update_idletasks()
time.sleep(1.0)
self.canvas.lower(list[0][0], list[0][1])
self.canvas.lower(list[1][0], list[1][1])
list.clear()

I finally got it to work!!
# ======================================= import statements
import tkinter as tk
import random
import PIL.Image as Image
import PIL.ImageTk as ImageTk
# ======================================= class def
class MemoryGame:
def __init__(self):
#initialize window
self.window = tk.Tk()
self.window.title("Sea Life Memory Game")
self.window.minsize(590, 600)
self.window.maxsize(590, 600)
#set main canvas as background
self.canvas = tk.Canvas(self.window, bg="lightblue",
bd=0, highlightthickness=0,
width=590, height=600)
self.canvas.grid(row=0, column=0)
self.canvas.bind("<Button-1>", self.chooseTile)
#establish coordinates for tiles and shuffle image placement
coordinates = [(5,30,105,130), (5,160,105,260), (5,290,105,390), (5,420,105,520), (125,30,225,130), (125,160,225,260), (125,290,225,390), (125,420,225,520), (245,30,345,130), (245,160,345,260), (245,290,345,390), (245,420,345,520), (365,30,465,130), (365,160,465,260), (365,290,465,390), (365,420,465,520), (485,30,585,130), (485,160,585,260), (485,290,585,390), (485,420,585,520)]
imageChoices = ['cropped images/001-turtle.png','cropped images/007-blowfish.png','cropped images/010-jellyfish.png','cropped images/011-starfish.png','cropped images/018-lobster.png','cropped images/028-fish.png','cropped images/033-walrus.png','cropped images/042-goldfish.png','cropped images/045-seal.png','cropped images/046-penguin.png']
random.shuffle(coordinates)
#write title to top of canvas
self.canvas.create_text(295, 15, text="Sea Life Memory Game!",
anchor="center", fill="white",
font="Times 24 bold")
#initialize counts
coordinateCount = 0
imageCount = 0
self.imageCollection = []
#for loop to attach images to each rectangle on the canvas
for i in range(len(imageChoices)):
x1, y1, x2, y2 = coordinates[coordinateCount]
self.image = ImageTk.PhotoImage(Image.open(imageChoices[imageCount]))
self.image.img = self.image
self.id = self.canvas.create_image(x1, y1, anchor="nw",
image=self.image.img)
self.imageCollection.append(self.id)
self.canvas.create_rectangle(x1, y1, x2, y2, fill="white", outline="white")
coordinateCount += 1
x1, y1, x2, y2 = coordinates[coordinateCount]
self.id = self.canvas.create_image(x1, y1, anchor="nw",
image=self.image.img)
self.canvas.create_rectangle(x1, y1, x2, y2, fill="white", outline="white")
coordinateCount += 1
imageCount += 1
self.imageCollection.append(self.id)
#create instructional text
self.canvas.create_text(295, 550, text="Find all the pairs as fast as possible.",
fill="white", font="Times 18", anchor="center")
self.canvas.create_text(295, 570, text="Click on a card to turn it over and find the same matching card.",
fill="white", font="Times 18", anchor="center")
def run(self):
self.window.mainloop()
global lst
lst = []
global matches
matches = 0
def chooseTile(self, event):
global lst
global matches
x = event.x
y = event.y
item = self.canvas.find_overlapping(x-1,y-1,x+1,y+1)
lst.append(item)
if len(lst) < 2:
self.canvas.tag_lower(lst[0][1], lst[0][0])
elif len(lst) == 2:
self.canvas.tag_lower(lst[1][1],lst[1][0])
if self.canvas.itemcget(lst[0][0], "image") == self.canvas.itemcget(lst[1][0], "image"):
matches += 2
lst.clear()
else:
self.window.update_idletasks()
self.window.after(1500)
self.canvas.lower(lst[0][0], lst[0][1])
self.canvas.lower(lst[1][0], lst[1][1])
lst.clear()
if matches == 20:
self.window.update_idletasks()
self.window.after(1000)
self.window.destroy()
# ======================================= script calls
game = MemoryGame()
game.window.mainloop()

Related

Drawing lines between two object in tkinter UI

Thanks to lots of help in Drag and drop the object in Tkinter UI, I could manage to draw three square that are draggable.
Now I am trying to draw 3 lines between each squares and I cannot find way to enable it. What I tried is following :
from tkinter import *
window = Tk()
window.state('zoomed')
window.configure(bg = 'white')
def drag(event):
new_x = event.x_root - window.winfo_rootx()
new_y = event.y_root - window.winfo_rooty()
event.widget.place(x=new_x, y=new_y,anchor=CENTER)
card = Canvas(window, width=10, height=10, bg='red1')
card.place(x=300, y=600,anchor=CENTER)
card.bind("<B1-Motion>", drag)
another_card = Canvas(window, width=10, height=10, bg='red2')
another_card.place(x=600, y=600,anchor=CENTER)
another_card.bind("<B1-Motion>", drag)
third_card = Canvas(window, width=10, height=10, bg='red3')
third_card.place(x=600, y=600,anchor=CENTER)
third_card.bind("<B1-Motion>", drag)
def line(x1, y1, x2, y2):
print(x1, y1, x2, y2)
Canvas.create_line(x1, y1, x2, y2, fill="green")
coor_1 = canvas.coords(card)
coor_2 = canvas.coords(another_card)
line(coor_1[0],coor_1[1],coor_1[0],coor_2[1])
window.mainloop()
It didn't work and I don't think it will work since this code does not catch the change occurred by dragging object, But I cannot guess how to code since I do not understand how the event function works completely. How should I make a code for it ?
for the purpose of self-study:
import tkinter as tk
window = tk.Tk()
window.state('zoomed')
class DragAndDropArea(tk.Canvas):
def __init__(self,master, **kwargs):
tk.Canvas.__init__(self,master, **kwargs)
self.active = None
card_I = self.draw_card(300,600, 100,100, 'red1')
card_II = self.draw_card(600,600, 100,100, 'red2')
card_III = self.draw_card(400,400, 100,100, 'red3')
self.bind_tention(card_I,card_III)
self.bind_tention(card_I,card_II)
self.bind_tention(card_III,card_II)
self.bind('<ButtonPress-1>', self.get_item)
self.bind('<B1-Motion>',self.move_active)
self.bind('<ButtonRelease-1>', self.set_none)
def set_none(self,event):
self.active = None
def get_item(self,event):
try:
item = self.find_withtag('current')
self.active = item[0]
except IndexError:
print('no item was clicked')
def move_active(self,event):
if self.active != None:
coords = self.coords(self.active)
width = coords[2] - coords[0] #x2-x1
height= coords[1] - coords[3] #y1-y2
position = coords[0],coords[1]#x1,y1
x1 = event.x - width/2
y1 = event.y - height/2
x2 = event.x + width/2
y2 = event.y + height/2
self.coords(self.active, x1,y1, x2,y2)
try:
self.update_tention(self.active)
except IndexError:
print('no tentions found')
def update_tention(self, tag):
tentions = self.find_withtag(f'card {tag}')
for tention in tentions:
bounded_cards = self.gettags(tention)
card = bounded_cards[0].split()[-1]
card2= bounded_cards[1].split()[-1]
x1,y1 = self.get_mid_point(card)
x2,y2 = self.get_mid_point(card2)
self.coords(tention, x1,y1, x2,y2)
self.lower(tention)
def draw_card(self, x,y, width,height, color):
x1,y1 = x,y
x2,y2 = x+width,y+height
reference = self.create_rectangle(x1,y1,x2,y2,
fill = color)
return reference
def bind_tention(self, card, another_card):
x1,y1 = self.get_mid_point(card)
x2,y2 = self.get_mid_point(another_card)
tag_I = f'card {card}'
tag_II= f'card {another_card}'
reference = self.create_line(x1,y1,x2,y2, fill='green',
tags=(tag_I,tag_II))
self.lower(reference)
def get_mid_point(self, card):
coords = self.coords(card)
width = coords[2] - coords[0] #x2-x1
height= coords[1] - coords[3] #y1-y2
position = coords[0],coords[1]#x1,y1
mid_x = position[0] + width/2
mid_y = position[1] - height/2
return mid_x,mid_y
area = DragAndDropArea(window, bg='white')
area.pack(fill='both',expand=1)
window.mainloop()

Is there a way to centre a canvas widget in the window?

I am building a chess program using Tkinker/Python. I am trying to align the board in the centre of the window so that I can have the taken pieces placed alongside the board. Any ideas?
from tkinter import *
root=Tk()
class Board():
def drawboard():
dark="#643c22"
light="#faeac6"
canvas=Canvas(root, width=920, height=720,)
canvas.pack( fill=BOTH)
colour=light
for row in range(8):
if colour==dark:
colour=light
else:
colour=dark
for column in range(8):
x1 = (column * 90)
y1 = ((7-row)* 90)
x2 = x1 + 90
y2 = y1 + 90
canvas.create_rectangle(x1, y1, x2, y2, fill=colour)
if colour==dark:
colour=light
else:
colour=dark
Board.drawboard()
root.mainloop()
I expect it to lined up in the centre but it is aligned to the left.
The class you created is simply a container for some functions... You probably need to read a little bit about object oriented python, and get acquainted with its specifics. I rewrote your class Board as an example; it inherits from tk.Tk and therefore is a tkinter root.
As far as the placement of the various widgets, I added a right and left frame in order to center the canvas representing the checkers board.
it comes like this:
import tkinter as tk
class Board(tk.Tk):
colours = ["#643c22", "#faeac6"]
def __init__(self, n=8):
super().__init__()
self.n = n
self.left_frame = tk.Frame(self)
self.left_frame.grid(row=0, column=0, rowspan=8, padx=100)
self.right_frame = tk.Frame(self)
self.right_frame.grid(row=0, column=10, rowspan=8)
self.canvas = tk.Canvas(self, width=920, height=720, )
self.canvas.grid(row=0, column=1, columnspan=8, rowspan=8)
self.board = [[None for row in range(n)] for col in range(n)]
self.current_colour_ndx = 0
def _swap_colours(self):
self.current_colour_ndx = (self.current_colour_ndx + 1) % 2
def drawboard(self):
for col in range(self.n):
self._swap_colours()
for row in range(self.n):
x1 = col * 90
y1 = (7-row) * 90
x2 = x1 + 90
y2 = y1 + 90
colour = self.colours[self.current_colour_ndx]
self.board[row][col] = self.canvas.create_rectangle(x1, y1, x2, y2, fill=colour)
self._swap_colours()
if __name__ == '__main__':
board = Board()
board.drawboard()
board.mainloop()

How to add image in tkinter gui?

I picked up this code and wanted to mess with it, the main problem i'm having is being unable to add an image to the actual gui at a set location on the 2 dimensional array. I get no actual error but also get no output of the image on the gui. Please help! Thank you.
import tkinter as tk
class GameBoard(tk.Frame):
def __init__(self, parent, rows=9, columns=9, size=60, color1="light grey", color2="light grey"):
'''size is the size of a square, in pixels'''
self.rows = rows
self.columns = columns
self.size = size
self.color1 = color1
self.color2 = color2
self.pieces = {}
canvas_width = columns * size
canvas_height = rows * size
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0,
width=canvas_width, height=canvas_height, background="white")
self.canvas.pack(side="top", fill="both", expand=True, padx=2, pady=2)
self.canvas.bind("<Configure>", self.refresh)
def addpiece(self, name, image, row=1, column=1):
bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
self.canvas.create_image(1,1, image=bishop, tags=(name, "Bishop"), anchor= "c")
self.placepiece(name, row, column)
def placepiece(self, name, row, column):
'''Place a piece at the given row/column'''
self.pieces[name] = (row, column)
x0 = (column * self.size) + int(self.size/2)
y0 = (row * self.size) + int(self.size/2)
self.canvas.coords(name, x0, y0)
def placepiece(self, name, row, column):
'''Place a piece at the given row/column'''
self.pieces[name] = (row, column)
x0 = (column * self.size) + int(self.size/2)
y0 = (row * self.size) + int(self.size/2)
self.canvas.coords(name, x0, y0)
def refresh(self, event):
'''Redraw the board, possibly in response to window being resized'''
xsize = int((event.width-1) / self.columns)
ysize = int((event.height-1) / self.rows)
self.size = min(xsize, ysize)
self.canvas.delete("square")
color = self.color2
for row in range(self.rows):
color = self.color1 if color == self.color2 else self.color2
for col in range(self.columns):
x1 = (col * self.size)
y1 = (row * self.size)
x2 = x1 + self.size
y2 = y1 + self.size
self.canvas.create_rectangle(x1, y1, x2, y2, outline="black", fill=color, tags="square")
color = self.color1 if color == self.color2 else self.color2
for name in self.pieces:
self.placepiece(name, self.pieces[name][0], self.pieces[name][1])
self.canvas.tag_raise("piece")
self.canvas.tag_lower("square")
if __name__ == "__main__":
root = tk.Tk()
board = GameBoard(root)
board.pack(side="top", fill="both", expand="true", padx=4, pady=4)
# player1 = tk.PhotoImage(data=imagedata)
# board.addpiece("player1", player1, 0,0)
root.mainloop()
You have a single line that tries to put an image to GUI:
self.canvas.create_image(1,1, image=bishop, tags=(name, "Bishop"), anchor= "c")
which is under addpiece.
The line below saves the image in a reference:
bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
which is defined exclusively for the scope of addpiece, when the method is finished, the image disappears if at all being displayed.
Which effectively makes this question a duplicate to Why does Tkinter image not show up if created in a function?
In order to prevent that, make the image reference available in the scope where mainloop is called.
Either attach the reference bishop to an object, for example self.canvas, whose reference is available in the scope of mainloop:
self.canvas.bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
self.canvas.create_image(1,1, image=self.canvas.bishop, tags=(name, "Bishop"), anchor= "c")
or simply pass the image reference from the mainloop scope is in, and use that object reference:
self.canvas.create_image(1,1, image=image, tags=(name, "Bishop"), anchor= "c")

Creating a crop tool for tkinter: The cropping tool crops in other places

I'm creating in tkinter a Crop Tool that is similar in Photoshop. This code has a function that is supposed to crop a moveable image within the cropping box (2 in x 2 in, passport size, and so on). The problem is, the code often crops portions of the image outside the box.
For example, if I have a portrait and aimed the face at the rectangle, the code would crop the hat instead, or anywhere but the face.
I tried to use bbox, event objects, etc. but the measurements end up wrong. Please help me. Thanks.
Here is a partial code. Sorry if it's a quite lengthy.
from tkinter import *
from tkinter import ttk
import tkinter as tk
from tkinter import messagebox
from tkinter.filedialog import askopenfilename, asksaveasfilename
from PIL import Image, ImageTk
class PictureEditor:
# Quits when called
#staticmethod
# Opens an image
def open_app(self, event=None):
self.canvas.delete(ALL)
# Opens a window to choose a file=
self.openfile = askopenfilename(initialdir = # "Filename here")
if self.openfile:
with open(self.openfile) as _file:
# if file is selected by user, I'm going to delete
# the contents inside the canvas widget
self.canvas.delete(1.0, END)
self.im = Image.open(self.openfile)
self.image = ImageTk.PhotoImage(self.im)
self.a1 = self.canvas.create_image(0, 0, anchor=NW,
image=self.image, tags="image")
self.image_dim = self.canvas.bbox(self.a1)
self.imx = self.image_dim[0]
self.imy = self.image_dim[1]
# updating text widget
window.update_idletasks()
def on_drag(self, event):
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
self.origx = event.x
self.origy = event.y
def on_release(self, event):
# when I release the mouse, this happens
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
self.newx = event.x
self.newy = event.y
# Measures mouse movement from one point to another
self.movex = self.origx - self.newx
self.movey = self.origy - self.newy
def on_motion(self, event):
# handles the dragging of an object
# compute how much the mouse has moved
delta_x = event.x - self._drag_data["x"]
delta_y = event.y - self._drag_data["y"]
# move the object the appropriate amount
self.canvas.move(self._drag_data["item"], delta_x, delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def draw(self, event, x1=None, y1=None,x2=None,y2=None):
# deleting contents of border, if any.
try:
self.canvas.delete(self.border)
except:
pass
# if an item is selected
selection = self.combo.get()
if selection == 'No Crop':
x1, y1, x2, y2 = None, None, None, None
if selection == '2 in x 2 in':
x1, y1, x2, y2 = self.imx, self.imy, self.imx + 200, self.imy + 200
if selection == '1 in x 1 in':
x1, y1, x2, y2 = self.imx, self.imy, self.imx + 100, self.imy + 100
if selection == 'Passport Size':
x1, y1, x2, y2 = self.imx, self.imy, self.imx + 132.28, self.imy
+170.079
if x1 != None or y1 != None or x2 != None or y2 != None:
self.dimensions = {"x1":x1, "y1":y1, "x2":x2, "y2":y2}
width = 5
self.border = self.canvas.create_rectangle(x1+ width, y1 +
width, x2 + width, y2 + width, width=width, outline="#ffffff", fill ="",
tags = "rectangle")
else:
pass
def crop(self, event=None):
# cropping the image
try:
self.crop_image = self.im.crop((self.dimensions["x1"] +
self.movex,
self.dimensions["y1"] + self.movey,
self.dimensions["x2"] + self.movex,
self.dimensions["y2"] + self.movey))
except:
print("cropping failed")
return 1
self.newly_cropped = ImageTk.PhotoImage(self.crop_image)
try:
new_image = self.canvas.create_image(120, 120,
image=self.newly_cropped)
print("Image is cropped")
except:
print("Cropping failed")
def __init__(self,window):
frame1 = Frame(bg='red')
frame1.pack(side=TOP, fill=X)
frame2height = 600
frame2width = 600
frame2 = Frame(window, bd=2, relief=SUNKEN)
frame2.pack(side=LEFT, fill=X)
frame3 = Frame(bg='green')
frame3.pack(side=LEFT, fill=X)
# Button that open pictures
open = Button(frame1, text='Open Pic', padx=20, command =
self.open_app)
open.pack(pady=5, padx=5, side=LEFT)
# Creating a canvas widget
self.canvas = tk.Canvas(frame2, height=frame2height,
width=frame2width,
bg='gray')
self.xsb = Scrollbar(frame2, orient="horizontal",
command=self.canvas.xview)
self.ysb = Scrollbar(frame2, orient="vertical",
command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.ysb.set,
xscrollcommand=self.xsb.set)
self.canvas.configure(scrollregion=(0, 0, 1000, 1000))
# keeps track of data being dragged
self._drag_data = {"x": 0, "y": 0, "item": None}
# creating image and crop border
self.canvas.tag_bind("image","<ButtonPress-1>", self.on_drag)
self.canvas.tag_bind("image","<ButtonRelease-1>", self.on_release)
self.canvas.tag_bind("image","<B1-Motion>", self.on_motion)
# widget positions in frame2
self.xsb.pack(side=BOTTOM, fill=X)
self.canvas.pack(side=LEFT)
self.ysb.pack(side=LEFT, fill=Y)
self.combo = ttk.Combobox(frame1)
# Combobox selections
self.combo['values'] = ('No Crop', '2 in x 2 in', '1 in x 1 in',
'Passport Size')
self.combo.current(0)
self.combo.pack(pady=5, padx=5, side=LEFT)
self.combo.bind("<Button-1>", self.draw)
# Button that crops picture
self.crop = Button(frame1, text='Crop Pic', padx=20,
command=self.crop)
self.crop.pack(pady=5, padx=5, side=LEFT)
# this window has all the properties of tkinter.
# .Tk() declares this variable as the frame
window = tk.Tk()
# .title() will input whatever title you want for the app
window.title("ID Picture Generator")
# .geometry() sets the size in pixels of what the window will be
window.geometry("800x600")
app = PictureEditor(window)
# runs everything inside the window
window.mainloop()

Python Tkinter. Best Fit Ellipse from Data Points

I am working on a Python GUI w/ Tkinter. I am trying to save four specified point locations from a BMP image into variables, and create a best-fit ellipse that more or less passes through the saved points. I am still a beginner working w/ Tkinter and GUI's so please bear w/ me!
So far, the code is able to mark the points and print out its position/coordinates. Should I use matplotlib for this kind of situation? Am i able to use that w/ tkinter as well?
Here is my code:
from tkinter import *
from PIL import Image, ImageTk
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.pos = []
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
self.counter = 0
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label="Exit", command=self.client_exit)
menu.add_cascade(label="File", menu=file)
analyze = Menu(menu)
analyze.add_command(label="Region of
Interest",command=self.regionOfInterest)
analyze.add_command(label="Erase", command=self.erasePoints)
menu.add_cascade(label="Analyze", menu=analyze)
load = Image.open("ap41.ddr.brf.sdat.bmp")
render = ImageTk.PhotoImage(load)
img = Label(self, image=render)
img.image = render
img.place(x=0, y=0)
def regionOfInterest(self):
root.config(cursor="plus")
canvas.bind("<Button-1>", self.imgClick)
def erasePoints(self):
self.pos = []
def client_exit(self):
exit()
def imgClick(self, event):
if self.counter < 4:
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
self.pos.append((x, y))
print(self.pos)
canvas.create_line(x - 5, y, x + 5, y, fill="red",
tags="crosshair")
canvas.create_line(x, y - 5, x, y + 5, fill="red",
tags="crosshair")
self.counter += 1
else:
canvas.unbind("<Button 1>")
root.config(cursor="arrow")
self.counter = 0
root = Tk()
imgSize = Image.open("ap41.ddr.brf.sdat.bmp")
tkimage = ImageTk.PhotoImage(imgSize)
w, h = imgSize.size
canvas = Canvas(root, width=w, height=h)
canvas.create_image((w/2,h/2),image=tkimage)
canvas.pack()
root.geometry("%dx%d"%(w,h))
app = Window(root)
root.mainloop()
Here is something you can play with and fine tune but I think this will be close to what you are trying to do.
first I created anther menu item labeled Create Ellipse that links to a method to work out the top left cords and bottom right cords and then uses that with the create_ovel() command to create an ellipse on the screen. Let me know if this is close to what you want to do.
The new method below will compare the values of each tuple to a base tuple and if the numbers are lower it will change the top left cords and if the numbers are high it will change the bottom right cords. With those 2 sets of cords figured out it will then create an ellipse to roughly fit what you selected.
def createEllipse(self):
top_left_cords = self.pos[0]
bottom_right_cords = self.pos[0]
for pos in self.pos:
if pos[0] < top_left_cords[0]:
top_left_cords = (pos[0], top_left_cords[1])
if pos[1] < top_left_cords[1]:
top_left_cords = (top_left_cords[0], pos[1])
if pos[0] > bottom_right_cords[0]:
bottom_right_cords = (pos[0], bottom_right_cords[1])
if pos[1] > bottom_right_cords[1]:
bottom_right_cords = (bottom_right_cords[0], pos[1])
Below is the full code:
from tkinter import *
from PIL import Image, ImageTk
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.pos = []
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
self.counter = 0
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label="Exit", command=self.client_exit)
menu.add_cascade(label="File", menu=file)
analyze = Menu(menu)
analyze.add_command(label="Region of Interest",command=self.regionOfInterest)
analyze.add_command(label="Erase", command=self.erasePoints)
analyze.add_command(label="Create Ellipse", command=self.createEllipse)
menu.add_cascade(label="Analyze", menu=analyze)
load = Image.open("./Colors/1.png")
render = ImageTk.PhotoImage(load)
img = Label(self, image=render)
img.image = render
img.place(x=0, y=0)
def createEllipse(self):
top_left_cords = self.pos[0]
bottom_right_cords = self.pos[0]
for pos in self.pos:
if pos[0] < top_left_cords[0]:
top_left_cords = (pos[0], top_left_cords[1])
if pos[1] < top_left_cords[1]:
top_left_cords = (top_left_cords[0], pos[1])
if pos[0] > bottom_right_cords[0]:
bottom_right_cords = (pos[0], bottom_right_cords[1])
if pos[1] > bottom_right_cords[1]:
bottom_right_cords = (bottom_right_cords[0], pos[1])
print(top_left_cords, bottom_right_cords)
canvas.create_oval(top_left_cords, bottom_right_cords)
def regionOfInterest(self):
root.config(cursor="plus")
canvas.bind("<Button-1>", self.imgClick)
def erasePoints(self):
self.pos = []
def client_exit(self):
exit()
def imgClick(self, event):
if self.counter < 4:
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
self.pos.append((x, y))
print(self.pos)
canvas.create_line(x - 5, y, x + 5, y, fill="red",
tags="crosshair")
canvas.create_line(x, y - 5, x, y + 5, fill="red",
tags="crosshair")
self.counter += 1
else:
canvas.unbind("<Button 1>")
root.config(cursor="arrow")
self.counter = 0
root = Tk()
imgSize = Image.open("./Colors/1.png")
tkimage = ImageTk.PhotoImage(imgSize)
w, h = imgSize.size
canvas = Canvas(root, width=w, height=h)
canvas.create_image((w/2,h/2),image=tkimage)
canvas.pack()
root.geometry("%dx%d"%(w,h))
app = Window(root)
root.mainloop()
Here is a before and after from a sample image I have for testing.
Before:
After:

Categories