Merging two applications using tk frames - python-3.x

I have a memory game made with python and tkinter. I've also made an application with a simple GUI that uses tk.Frame to show different frames. My problem is putting the memory game in one of the frames of the GUI app.
I have one .py file for the memory game and one for the GUI. The GUI has multiple classes and frames. I want to put the memory game inside one of those frames so that you can navigate through the menu into the game. The game should only show when navigated to.
I have tried:
importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
copying the whole memorygame code into a class of the GUI file
Importing the file made both the applications try to run on startup in different windows. Copying the game code into a GUI class gave lots of errors.
I have python 3.7 and tkinter 8.6
The memory game starts by creating a canvas that it draws upon:
win = tk.Tk()
canvas = tk.Canvas(win, width = 600, height = 480)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "green")
...
And this is how I use a class to create a frame where I display different things:
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# memory game goes here
I can show the entire files if anyone wants to see
I want the game to only show until I click on a button that takes me to the frame where I want the game.
Edit: the whole memory game file
import tkinter as tk
import time
from random import randint
from random import shuffle
win = tk.Tk()
canvas = tk.Canvas(win, width = 500, height = 500)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
tiles = []
colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(colors) - 1)
color = colors[randomInd]
selected.append(color)
selected.append(color)
del colors[randomInd]
shuffle(selected)
flippedTiles = []
def mouseClicked(self):
global numFlipped
global flippedTiles
for i in range(len(tiles)):
if tiles[i].isUnderMouse(self):
if (len(flippedTiles) < 2 and not(tiles[i].isFaceUp)) :
tiles[i].drawFaceUp()
flippedTiles.append(tiles[i])
if (len(flippedTiles) == 2):
if not(flippedTiles[0].text == flippedTiles[1].text):
time.sleep(1)
flippedTiles[0].drawFaceDown()
flippedTiles[1].drawFaceDown()
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0,NUM_COLS):
for y in range(0,NUM_ROWS):
tiles.append(Tile(x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(tiles)):
tiles[i].drawFaceDown()
flippedThisTurn = 0
def mouseClicked(event):
global flippedTiles
global flippedThisTurn
for tile in tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
flippedTiles.append(tile)
flippedThisTurn += 1
if (flippedThisTurn == 2):
win.after(1000, checkTiles)
flippedThisTurn = 0
def checkTiles():
if not(flippedTiles[-1].text == flippedTiles[-2].text): #check last two elements
flippedTiles[-1].drawFaceDown() #facedown last two elements
flippedTiles[-2].drawFaceDown()
del flippedTiles[-2:] #remove last two elements
win.bind("<Button-1>", mouseClicked)
win.mainloop()

importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
Both these ways will make "both the applications try to run on startup in different windows" this is because the memorygame file makes a new tk.Tk() window, and so does your GUI handler.
copying the whole memorygame code into a class of the GUI file
This could cause many problems, depending where you copy the files to, since these files have their own import dependencies and the import paths may change depending where you copy the files to.
What I would suggest doing is the following, I would change your code of the memory game to make the memory game a class(tk.Frame) (Frame class), then you should be able to import the memory game and stick that frame into the GUI PageMk(tk.Frame), I do not know the dependencies your code has, but this should work.
Example of change using the provided code
class MemGame(tk.Frame):
def __init__(self, parent):
super(MemGame, self).__init__(parent)
self.configure(width=600, height=480)
canvas = tk.Canvas(self, width=600, height=480, bg="red")
canvas.pack()
class Tile:
def __init__(self, parent, x, y, text):
self.parent = parent
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.parent.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill="green")
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
My Edits to the full code:
import tkinter as tk
import time
from random import randint
from random import shuffle
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("500x400")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, text):
self.canvas = canvas
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.configure(width=500, height=500)
self.canvas = tk.Canvas(self, width=500, height=500)
self.canvas.pack()
self.tiles = []
self.colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.colors) - 1)
color = self.colors[randomInd]
selected.append(color)
selected.append(color)
del self.colors[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.bind("<Button-1>", self.mouseClicked)
# def mouseClicked(self):
# for i in range(len(self.tiles)):
# if self.tiles[i].isUnderMouse(self):
# if (len(self.flippedTiles) < 2 and not(self.tiles[i].isFaceUp)) :
# self.tiles[i].drawFaceUp()
# self.flippedTiles.append(self.tiles[i])
# if (len(self.flippedTiles) == 2):
# if not(self.flippedTiles[0].text == self.flippedTiles[1].text):
# time.sleep(1)
# self.flippedTiles[0].drawFaceDown()
# self.flippedTiles[1].drawFaceDown()
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
self.after(1000, self.checkTiles)
self.flippedThisTurn = 0
def checkTiles(self):
if not(self.flippedTiles[-1].text == self.flippedTiles[-2].text): #check last two elements
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
if __name__ == '__main__':
c = Controller()
c.mainloop()
Goodluck :)

Related

How to resize a list of images (100), shown 2 by 2 in the same window and they have to change size according to the destination, enlarge the window?

I have a list of 100 images that are displayed in labels and when I press a button in the main window, these images that are displayed in the top-level window, they change to another set of images, so I use the FRAME class to switch to another FRAME while pressing a button. The whole system to change images so that they do not repeat, I have already done it, but repeating a method to resize each image 100 times is not appropriate, I try with a for loop but I do not get it. Please help me I'm stuck
class Example(Frame):
def __init__(self, master, array, *args):
Frame.__init__(self, master, *args)
self.image = Image.fromarray(array) ####---> ERROR
self.img_copy = self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill='both', expand=True)
self.background.bind('<Configure>', self._resize_image)
def _resize_image(self, event):
new_width = event.width
new_height = event.height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image=self.background_image)
class Applications(Tk):
def __init__(self):
Tk. __init__(self)
self.path = "E:/1-RICHI/MovilesDB"
self.Images = self.files(self.path, "ever")
self.Images_copy = self.files(self.path, "partial")
self.example = Creator() ####---> ERROR
self.example .pack()
def files(self, path, option): # Generate list
images = os.listdir(path)
self.list_partial= []
self.list_ever= []
if option == "ever":
for i in images:
if ".jpg" in i:
route= path + "/" + i
open = cv2.imread (route)
RGB = cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
objet = Image.fromarray(RGB)
#photo = ImageTk.PhotoImage(objet)
self.list_ever.append(objet)
return self.list_ever
if option == "partial" :
for i in images:
if ".jpg" in i:
route = path + "/" + i
open = cv2.imread(route)
RGB = cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
objet = Image.fromarray(RGB)
self.list_partial.append(objet)
return self.list_partial
class Creator(Frame):
def __init__(self,*args, **kwargs):
Frame.__init__(self,*args, **kwargs)
self.LIST=[]
for i in self.master.Images:
Example(self, i) .place(x=0, y=0, relwidth=1, relheight=1) ####---> ERROR
if __name__=="__main__":
app_1 = Applications()
app_1.mainloop()
You can try adding these changes to your Creator class (first just try copy-pasting and see if this works, then if you have questions, ask them):
from tkinter import Tk, Frame, Label
from PIL import Image, ImageTk
class Example(Frame):
def __init__(self, master, path, *args):
Frame.__init__(self, master, *args)
self.image = Image.open(path)
self.img_copy = self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill='both', expand=True)
self.background.bind('<Configure>', self._resize_image)
def _resize_image(self, event):
new_width = event.width
new_height = event.height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image=self.background_image)
def change_images(index):
if index >= len(img_lst):
index = 0
img_lst[index].lift()
root.after(1000, change_images, index + 1)
path_lst = ['pause_btn.png', 'play_btn.png', 'space.jpg'] # change/add paths here
img_lst = []
root = Tk()
for path in path_lst:
e = Example(root, path)
e.place(x=0, y=0, relwidth=1, relheight=1)
img_lst.append(e)
change_images(0)
root.mainloop()
In your code (remember to import other stuff too):
from tkinter import Tk, Frame, Label
from PIL import Image, ImageTk
class Example(Frame):
def __init__(self, master, array, *args):
Frame.__init__(self, master, *args)
self.image = Image.fromarray(array)
self.img_copy = self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill='both', expand=True)
self.background.bind('<Configure>', self._resize_image)
def _resize_image(self, event):
new_width = event.width
new_height = event.height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image=self.background_image)
class Applications(Tk):
def __init__(self):
Tk. __init__(self)
self.path="E:/1-RICHI/MovilesDB"
self.Images=self.files(self.path, "ever") # list one
self.Images_copy=self.files(self.path, "partial") # list two
self._toplevel()
def files(self, path, option): # here I generate the lists
images=os.listdir(path). # route
self.list_partial=[] # internal list
self.list_ever=[] # internal list
if option == "ever":
for i in images:
if ".jpg" in i:
route=path + "/" + i
open=cv2.imread (route)
if open is None:
continue
RGB=cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
self.list_ever.append(RGB)
return self.list_ever
if option == "partial" :
for i in images:
if ".jpg" in i:
route=path + "/" + i
open=cv2.imread(route)
if open is None:
continue
RGB=cv2.cvtColor(open, cv2.COLOR_BGR2RGB)
objet=Image.fromarray(RGB)
self.list_partial.append(objet)
return self.list_partial
def _toplevel(self):
top1=Toplevel()
frame=Creator()
frame.pack()
class Creator(Frame):
def __init__(self,*args, **kwargs):
Frame.__init__(self,*args, **kwargs)
self.LIST=[] # img_lst
for i in self.master.Images:
Example(self, i).place(x=0, y=0, relwidth=1, relheight=1)
if __name__=="__main__":
app_1 =Applications()
app_1.mainloop()

PyQt5 - Custom QWidget not showing up on parent widget

I am working on a game in which I have completed the engine and the networking layers. I am now working on some GUI for the game using PyQt5 (as the drawing isn't complex). I made a custom QWidget for drawing the game board which is just a widget that positions custom QPushButtons inside itself. Here's the code for that:
class BoardButton(QtWidgets.QPushButton):
tilePressed = pyqtSignal(Tile)
def __init__(self, tile: Tile = None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAutoFillBackground(True)
self.setCheckable(False)
self.setDefault(False)
self.setFlat(False)
self.raise_()
self.tile = tile
self.default_background(tile)
self.highlight = False
self.is_clicked = False
def __change_bgd(self, colour):
p = self.palette()
p.setColor(self.backgroundRole(), colour)
self.setPalette(p)
def default_background(self, tile: Tile):
if tile == self.tile:
self.__change_bgd(QtGui.QColor(153, 76, 0))
def highlight_background(self, tile: Tile):
if tile == self.tile:
self.__change_bgd(QtCore.Qt.green)
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
self.tilePressed.emit(self.tile)
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
if self.highlight:
self.highlight_background(self.tile)
else:
self.default_background(self.tile)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setPen(QtGui.QPen(QtCore.Qt.black, QtCore.Qt.SolidLine))
painter.drawRect(0, 0, self.width(), self.height())
if self.tile:
if self.tile.is_special:
first_line = self.width() / 3
second_line = first_line * 2
painter.drawLine(first_line, 0, first_line, self.height())
painter.drawLine(second_line, 0, second_line, self.height())
painter.drawLine(0, first_line, self.width(), first_line)
painter.drawLine(0, second_line, self.width(), second_line)
if self.tile.is_exit:
painter.setBrush(QtCore.Qt.NoBrush)
painter.drawEllipse(self.width() / 4, self.height() / 4, self.width() / 2, self.height() / 2)
if self.tile.piece is not None:
if self.tile.piece.is_white:
colour = QtCore.Qt.white
else:
colour = QtCore.Qt.black
painter.setBrush(QtGui.QBrush(colour, QtCore.Qt.SolidPattern))
painter.drawEllipse(0, 0, self.width(), self.height())
if self.tile.piece.is_king:
painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
painter.drawEllipse(self.width() / 4, self.height() / 4, self.width() / 2, self.height() / 2)
class GameBoard(QtWidgets.QWidget):
tilePressed = pyqtSignal(Tile)
boardUpdate = pyqtSignal()
def __init__(self, game: Game = None, is_white: bool = None, playable=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setGeometry(QtCore.QRect(20, 20, 650, 650))
self.setObjectName("boardWidget")
self.game = game
self.is_white = is_white
self.playable = playable
self.buttons = []
self.__selected = None
self.tilePressed.connect(self.__tile_pressed)
def init_buttons(self):
def pos_to_name(x, y):
return f'btn_{x}{y}'
self.buttons = []
board = self.game.board
board_size = board.width
if board.width > board.height:
board_size = board.width
elif board.width < board.height:
board_size = board.height
btn_size = self.width() / board_size
start_x = self.width() / 2 - btn_size * (board.width / 2)
start_y = self.height() / 2 - btn_size * (board.height / 2)
for y in range(board.height):
row = []
for x in range(board.width):
button = BoardButton(board[y][x], parent=self)
button.setGeometry(QtCore.QRect(start_x + x * btn_size, start_y + y * btn_size, btn_size, btn_size))
button.setObjectName(pos_to_name(x, y))
button.tilePressed.connect(self.tilePressed.emit)
row.append(button)
self.buttons.append(row)
def __get_button(self, tile: Tile) -> BoardButton:
return self.buttons[tile.y][tile.x]
def update(self) -> None:
self.boardUpdate.emit()
for y, row in enumerate(self.buttons):
for x, button in enumerate(row):
button.tile = self.game.board[y][x]
button.update()
super().update()
def remove_highlights(self):
self.__selected = None
for row in self.buttons:
for button in row:
button.highlight = False
button.is_clicked = False
def highlight_buttons(self, source_tile: Tile):
self.__selected = source_tile
for tile in self.game.board.valid_moves(source_tile):
self.buttons[tile.y][tile.x].highlight = True
def move_piece(self, target_tile: Tile):
if not self.__selected:
raise ValueError('Cannot move piece because no piece is selected.')
self.game.move(self.__selected, target_tile)
def is_highlight(self, tile: Tile) -> bool:
return self.__get_button(tile).highlight
def is_clicked(self, tile: Tile) -> bool:
return self.__get_button(tile).is_clicked
def set_clicked(self, tile: Tile, value: bool):
self.__get_button(tile).is_clicked = value
def __tile_pressed(self, tile: Tile):
if not tile or self.game.game_over or not self.playable:
return
if self.is_white is not None and not is_turn(self.is_white, self.game):
return
if not tile.piece and self.is_highlight(tile):
try:
self.move_piece(tile)
except (BoardGameException, Win):
pass
self.remove_highlights()
elif not tile.piece and not self.is_highlight(tile):
self.remove_highlights()
elif tile.piece:
if self.is_clicked(tile):
self.remove_highlights()
elif self.game.is_turn(tile):
self.remove_highlights()
self.highlight_buttons(tile)
self.set_clicked(tile, True)
self.update()
This code works perfectly with one of my windows:
class LocalGameWindow(Ui_FrmLocalGame):
def __init__(self, game, playable=True):
super().__init__(game=game, playable=playable)
self.init_gameboard()
self.gameboard.update()
if self.gameboard.playable:
self.btnUndo.clicked.connect(self.btn_undo_clicked)
else:
self.setWindowTitle(...)
self.btnUndo.setText('Play')
self.lblTurn.setVisible(False)
self.lblBlackPieces.setVisible(False)
self.lblWhitePieces.setVisible(False)
self.btnExit.clicked.connect(self.close)
def btn_undo_clicked(self):
self.gameboard.game.undo()
self.gameboard.update()
Which inherits from the following (Ui_FrmLocalGame was generated with Qt Designer):
class Ui_FrmLocalGame(_GameBoardWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setupUi(self)
def setupUi(self, FrmLocalGame):
...
Which in turn inherits from:
class _GameBoardWindow(GameWidget):
def __init__(self, client: Client = None, *args, **kwargs):
super().__init__()
is_white = None
if client:
is_white = client.is_white
self.gameboard = GameBoard(is_white=is_white, *args, **kwargs, parent=self)
self.client = client
self.gameboard.boardUpdate.connect(self.update_labels)
def init_gameboard(self, game: Game = None):
if game:
self.gameboard.game = game
self.gameboard.init_buttons()
def update_labels(self):
game = self.gameboard.game
if not game:
return
if not game.game_over:
text = f"{'Black' if game.black.is_turn else 'White'}'s Turn"
else:
text = f"{'Black' if game.black.won else 'White'} Won!"
self.lblTurn.setText(text)
self.lblBlackPieces.setText(f'Black: {game.board.num_black}/{game.board.num_start_black}')
self.lblWhitePieces.setText(f'White: {game.board.num_white}/{game.board.num_start_white}')
This gives me the result I expect, it draws the gameboard widget on the window widget:
However, when I try to do seemingly the same thing with a different window widget (the online window), it just doesn't appear on the window as it does with the local game window. Here's the code for the window:
class Ui_FrmOnlineGame(_GameBoardWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setupUi(self)
def setupUi(self, FrmOnlineGame):
...
class OnlineGameWindow(Ui_FrmOnlineGame):
def __init__(self, client: Client):
super().__init__(client=client)
self.btnSend.clicked.connect(self.btn_send_clicked)
self.txtChat.returnPressed.connect(self.btn_send_clicked)
self.btnExit.clicked.connect(self.close)
self.__timer = QtCore.QTimer(self)
self.__timer.setInterval(100)
self.__timer.timeout.connect(self.__game_loop)
self.__timer.start()
def __game_loop(self):
try:
msg = self.client.recv_msg()
except BlockingIOError:
return
...
elif msg.startswith(Message.GameUpdate.value):
game = Game.from_serial(msg[1:])
self.gameboard.game = game
if not len(self.gameboard.buttons):
self.init_gameboard()
self.gameboard.update() # This should draw the gameboard widget like it does on the other window
...
def display_chat(self, text: str):
...
def display_colour(self):
show_dialog(f"You are {'white' if self.client.is_white else 'black'}.", self, 'Game Start', modal=False)
def btn_send_clicked(self):
...
def close(self) -> bool:
self.__timer.stop()
self.client.exit()
return super().close()
I've tried many things such as checking the custom widget's geometry, raising it, and making sure that it does have a parent (which it does). What have I done wrong? Any help would be appreciated, and sorry about all the code, I just feel it's necessary to find the issue.

Cropping multiple parts of the image and placing on the canvas in Tkinter

I am new to the Tkinter platform so please help me where I'm going wrong.
I have a floorplan image in which I want to cut the objects in it and place it on a canvas screen so that individual objects can be dragged if I want.
I'm able to cut and paste the objects on the screen except the first object. It is not placed on the screen. Can anyone help me?
I am using a Matlab code to identify the objects in the floorplan image. I am attaching the Matlab file.
Is it possible to add the wall to the screen? I have no idea at all. Can anyone suggest how to add the wall?
Here is my code
import tkinter as tk
from tkinter import *
from PIL import Image,ImageTk
from scipy.io import loadmat
root = tk.Tk()
canvas = tk.Canvas(width=800, height=800)
canvas.grid(row=4,column=0,sticky=(N,W,E,S))
#canvas.config(width=100,height=100)
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(4, weight=1)
mfile=loadmat('C:\\Users\\User\\Desktop\\tkinter_codes\\obj identification\\ans.mat')
#print(mfile.values())
#print(len(mfile['ans'][0]))
print(mfile.values())
class Example(tk.Frame):
def __init__(self, parent):
self.parent =parent
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(width=800, height=800)
self.canvas.grid(row=0,column=0,sticky=(N,W,E,S))
#canvas.pack (expand =1, fill =tk.BOTH)
self.canvas.tag_bind("DnD","<Button-1>")
self._drag_data = {"x": 0, "y": 0, "item": None}
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
self.canvas.bind("<ButtonPress-1>", self.on_button_1)
self.iimg=Image.open("C:\\Users\\User\\Desktop\\tkinter_codes\\floorplans\\ROBIN\\Dataset_3rooms\\Cat1_1.jpg")
#iimg=iimg.resize((1000, 800), Image.ANTIALIAS)
self.canvas.img=ImageTk.PhotoImage(self.iimg)
#canvas.img = canvas.img.resize((250, 250), Image.ANTIALIAS)
self.canvas_img=canvas.create_image(0,0,image=self.canvas.img,anchor="nw")
self.mylist=[]
for x in range(len(mfile['ans'][0])):
#canvas.create_rectangle((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]),outline='red',tags=("token","DnD"))
self.im_crop = self.iimg.crop((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]))
self.canvas.im_crop2=ImageTk.PhotoImage(self.im_crop)
self.canvas.create_image(mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1], image=self.canvas.im_crop2)
#canvas.create_image(1000,1000,image=im_crop2)
#if(x>=0):
self.mylist.append(self.canvas.im_crop2)
#im_crop.show()
#canvas.iiiimg=ImageTk.PhotoImage(im_crop)
#canvas.create_image(150,150,image=canvas.im_crop2)
self.popup = tk.Menu(root, tearoff=0)
#self.popup.add_command(label="delete",command=lambda: self.dele(id))
self.popup.add_command(label="delete",
command=lambda: self.dele(id))
self.popup.add_command(label="add",command= lambda: root.bind("<Button-1>",self.addn))
root.bind("<Button-3>", self.do_popup)
self.canvas.delete(self.canvas_img)
def do_popup(self,event):
# display the popup menu
try:
self.popup.tk_popup(event.x_root, event.y_root, 0)
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.popup.grab_release()
def on_button_1(self, event):
iid = self.canvas.find_enclosed(event.x-150, event.y-150, event.x + 150, event.y + 100)
#iid=canvas.find_closest(event.x, event.y)[0]
self.canvas.itemconfigure("DEL")
self.canvas.dtag("all","DEL")
self.canvas.itemconfigure(iid, tags=("DEL","DnD","token","drag"))
#canvas.unbind("<Button-1>")
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_rectangle(
x ,
y ,
x + 50,
y + 50,
outline=color,
fill=color,
tags=("token","DnD"),
)
def create_token1(self,x,y,color):
self.canvas.create_rectangle(
x ,
y ,
x + 25,
y + 25,
outline=color,
fill=color,
tags=("token","DnD"),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
rect=self.canvas.bbox(self._drag_data["item"])
self.canvas.addtag_enclosed("drag",*rect)
print(rect)
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
self.canvas.dtag("drag","drag")
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
self.delta_x = event.x - self._drag_data["x"]
self.delta_y = event.y - self._drag_data["y"]
# move the object the appropriate amount
self.canvas.move("drag", self.delta_x, self.delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def dele(self,id):
#canvas.tag_bind(id,"<Button-1>")
self.canvas.delete("DEL")
def addn(self,event):
canvas.create_rectangle(event.x,event.y,event.x+25,event.y+25,fill='red',tags=("DnD","token"))
root.unbind("<Button-1>")
Example(root) #pack(fill="both", expand=True)
root.mainloop()
This is the Matlab code I am using for identifying objects

How to combine lists or alternatively create list of lists

I have a memory game where you have to find pairs of tiles with identical images. I want to modify the game so that you have to find pairs of a tile with image and a tile with corresponding text. For example find a tile with an image of a cat and a tile with the text "cat".
To understand the problem, here's the code that I believe is most relevant, showing how I currently use a list of images to create pairs of tiles:
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.image = image
more stuff...
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.images = [
photoDog,
more images...
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
I'm not sure if I should create another list of texts, or change the list of images to a list of lists with an image and a text.
In case it's needed, here's the full code:
import tkinter as tk
from random import randint
from random import shuffle
import pygame
pygame.init()
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("800x480")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.cardback = cardback
self.canvas = canvas
self.y = y
self.x = x
self.image = image
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.cardback)
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.image)
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 100):
if(event.y > self.y and event.y < self.y + 100):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
photoRestart = tk.PhotoImage(file="restart.png")
imgRestart = tk.Label(self, anchor="s", image=photoRestart)
imgRestart.image = photoRestart
buttonRestart = tk.Button(self, activebackground="white",
image=photoRestart, highlightthickness=0, borderwidth=0,
command=lambda: self.restart())
buttonRestart.place(x=560, y=200)
photoDog = tk.PhotoImage(file="Dyr/dog.png")
photoElefant = tk.PhotoImage(file="Dyr/elefant.png")
photoFlamingo = tk.PhotoImage(file="Dyr/flamingo.png")
photoFlodhest = tk.PhotoImage(file="Dyr/flodhest.png")
photoKamel = tk.PhotoImage(file="Dyr/kamel.png")
photoKatt = tk.PhotoImage(file="Dyr/katt.png")
photoKroko = tk.PhotoImage(file="Dyr/krokodille.png")
photoNeshorn = tk.PhotoImage(file="Dyr/neshorn.png")
photoSkilpadde = tk.PhotoImage(file="Dyr/skilpadde.png")
photoStruts = tk.PhotoImage(file="Dyr/struts.png")
photoZebra = tk.PhotoImage(file="Dyr/zebra.png")
photoLove = tk.PhotoImage(file="Dyr/love.png")
photoCardback = tk.PhotoImage(file="cardback.png")
self.cardback = photoCardback
self.riktig_sound = pygame.mixer.Sound("riktig.wav")
self.click_sound = pygame.mixer.Sound("camerashutter.wav")
self.configure(width=650, height=480, bg="white")
self.canvas = tk.Canvas(self, bg="white", width=550, height=480, highlightthickness=0, borderwidth=0)
self.canvas.place(x=0, y=-30)
self.tiles = []
self.images = [
photoDog,
photoElefant,
photoFlamingo,
photoFlodhest,
photoKamel,
photoKatt,
photoKroko,
photoNeshorn,
photoSkilpadde,
photoStruts,
photoZebra,
photoLove
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.canvas.bind("<Button-1>", self.mouseClicked)
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event) and (self.flippedThisTurn < 2):
if (not(tile.isFaceUp)):
self.clickSound()
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
if (self.flippedTiles[-1].image == self.flippedTiles[-2].image): #check last two elements
self.riktig()
self.after(1000, self.checkTiles)
def checkTiles(self):
self.flippedThisTurn = 0
if not(self.flippedTiles[-1].image == self.flippedTiles[-2].image):
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
def restart(self):
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
def riktig(self):
pygame.mixer.Sound.play(self.riktig_sound)
pygame.mixer.music.stop()
def clickSound(self):
pygame.mixer.Sound.play(self.click_sound)
pygame.mixer.music.stop()
if __name__ == '__main__':
c = Controller()
c.mainloop()
I think the easiest approach, which causes the least changes to your current code is to:
Make your list of images (done)
Make a list of text-based corresponding tiles (to do)
Make a combined list of the two above and draw each image only once instead of twice as you are doing now (easy)
Make a dictionary that you can use to look up matches (see below)
Modify your "check tiles" function to see if they match using the dictionary (one-line change)
The dictionary created can be used bi-directionally so it will find match regardless of how comparison is done.
In [6]: image_tiles = ['dog_img', 'cat_img', 'hen_img']
In [7]: tag_tiles = ['dog', 'cat', 'hen']
In [8]: all_tiles = image_tiles + tag_tiles # use this to draw tiles
In [9]: matches = { k:v for (k, v) in zip(image_tiles, tag_tiles) }
In [10]: # add opposite lookup
In [11]: matches.update([(k, v) for (k, v) in zip(tag_tiles, image_tiles)])
In [12]: matches
Out[12]:
{'dog_img': 'dog',
'cat_img': 'cat',
'hen_img': 'hen',
'dog': 'dog_img',
'cat': 'cat_img',
'hen': 'hen_img'}

Tkinter - How to move image from canvas in slow motion

guys. I am trying to create my own version of a card game. I got the following problem trying to move my cards to the center of the canvas on click event. Here is an example of my code
import tkinter as tk
class gui(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.canvas = tk.Canvas(parent, bg="blue", highlightthickness=0)
self.canvas.pack(fill="both", expand=True)
self.img = PhotoImage(file="card.gif")
self.card = self.canvas.create_image(10, 10, image=self.img)
self.canvas.tag_bind(self.card, '<Button-1>', self.onObjectClick1)
def onObjectClick1(self, event):
if self.canvas.find_withtag("current"):
x = 400
y = 400
self.canvas.coords("current", x, y)
self.canvas.tag_raise("current")
if __name__ == "__main__":
root = tk.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
gui(root)
root.mainloop()
What I want is to move my card but not just move from one coordinate to another but giving it in slow motion effect.
The basic idea is to write a function that moves an object a small amount, and then schedules itself to be called again after a short delay. It does this until it reaches its destination.
Here is a very simple example that moves a couple items independently. You can adjust the speed by changing the speed parameter, or by changing the values of delta_x and delta_y.
This is a very simplistic algorithm that just increases the x and y coordinates by a fixed amount. You could instead calculate equally spaced points along a curve or straight line. Regardless, the animation technique remains the same.
import Tkinter as tk
def move_object(canvas, object_id, destination, speed=50):
dest_x, dest_y = destination
coords = canvas.coords(object_id)
current_x = coords[0]
current_y = coords[1]
new_x, new_y = current_x, current_y
delta_x = delta_y = 0
if current_x < dest_x:
delta_x = 1
elif current_x > dest_x:
delta_x = -1
if current_y < dest_y:
delta_y = 1
elif current_y > dest_y:
delta_y = -1
if (delta_x, delta_y) != (0, 0):
canvas.move(object_id, delta_x, delta_y)
if (new_x, new_y) != (dest_x, dest_y):
canvas.after(speed, move_object, canvas, object_id, destination, speed)
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()
item1 = canvas.create_rectangle(10, 10, 30, 30, fill="red")
item2 = canvas.create_rectangle(360, 10, 380, 30, fill="green")
move_object(canvas, item1, (200, 180), 25)
move_object(canvas, item2, (200, 220), 50)
root.mainloop()
In order to 'animate' your cards moving, a system of breaking down the total distance to be moved, and then moving/updating by smaller distances over a time-period would work.
For example, if you wish to move a card 400 units in x & y, something like this could work:
total_time = 500 #Time in milliseconds
period = 8
dx = 400/period
dy = 400/period
for i in range(period):
self.canvas.move(chosen_card, dx, dy)
root.after(total_time/period) #Pause for time, creating animation effect
root.update() #Update position of card on canvas
This could be a basic premise for an animation. Of course you would need to edit the total_time and period variables in my example to create what you feel is right.
This code below (ready for copy/paste and run as it is) gives a nice smooth motion on my box:
import tkinter as tk
import time
class gui(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.canvas = tk.Canvas(parent, bg="blue", highlightthickness=0)
self.canvas.pack(fill="both", expand=True)
self.img = tk.PhotoImage(file="card.gif")
self.card = self.canvas.create_image(10, 10, image=self.img)
self.canvas.tag_bind(self.card, '<Button-1>', self.onObjectClick1)
def onObjectClick1(self, event):
if self.canvas.find_withtag("current"):
x = 400
y = 400
self.canvas.coords("current", x, y)
self.canvas.tag_raise("current")
total_time = 500 #Time in milliseconds
period = 400
dx = 400/period
dy = 400/period
for i in range(period):
self.canvas.move(self.card, dx, dy) # chosen_card
time.sleep(0.01)
# root.after(total_time/period) #Pause for time, creating animation effect
root.update() #Update position of card on canvas
if __name__ == "__main__":
root = tk.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
gui(root)
root.mainloop()

Resources