I am trying to make a janggi(chess variant) game using flet. I wanted to use drag and drop for the pieces, so I followed the Solitaire tutorial on the flet docs. The problem occured when I tried to do the "move_on_top" part. Before adding the move_on_top code, things worked perfectly as expected. I could move the pieces around freely. However after I added the move_on_top function, It works sometimes, but it was also causing the AssertionError randomly, and the drag is not working anymore.
Future exception was never retrieved
future: <Future finished exception=AssertionError('Control must be added to the page first.')>
Traceback (most recent call last):
File "C:\Users\andyp\AppData\Local\Programs\Python\Python312\Lib\concurrent\futures\thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\andyp\AppData\Local\Programs\Python\Python312\Lib\site-packages\flet_core\page.py", line 950, in wrapper
handler(*args)
File "c:\limbaksa\janggi-v2\janggiBoard.py", line 22, in drag
e.control.update()
File "C:\Users\andyp\AppData\Local\Programs\Python\Python312\Lib\site-packages\flet_core\control.py", line 314, in update
assert self.__page, "Control must be added to the page first."
^^^^^^^^^^^
AssertionError: Control must be added to the page first.
I thought that this had to do with move_on_top function. I think the error is occuring when I try to update them after the function remove and re-append the piece to the controls. But I do not understand why this is happening since the function adds the piece back into the controls after it removes it. My code is here, I simplified it as much as I could. There are 90 invisible slots, and pieces are placed on the slots according to the board. The janggibase.Board has informations about the board such as the list of pieces on the board. The janggibase.Piece contains informations about the piece such as it's location. I also uploaded the full code to [my github](github.com/limbaksa/janggi-v2/)
import flet as ft
import janggibase
def move_on_top(piece,controls):
controls.remove(piece)
controls.append(piece)
piece.board.update()
def start_drag(e:ft.DragStartEvent):
move_on_top(e.control,e.control.board.controls)
e.control.board.move_start_top=e.control.top
e.control.board.move_start_left=e.control.left
def bounce_back(board,piece):
piece.top=board.move_start_top
piece.left=board.move_start_left
board.update()
def drag(e: ft.DragUpdateEvent):
e.control.top = max(0, e.control.top + e.delta_y)
e.control.left = max(0, e.control.left + e.delta_x)
e.control.update()
def drop(e:ft.DragEndEvent):
for slot in e.control.board.slots:
if (
abs(e.control.top - slot.top) < 20
and abs(e.control.left - slot.left) < 20
):
place(e.control, slot)
break
else:
bounce_back(e.control.board,e.control)
e.control.update()
def place(piece,slot):
piece.top=slot.top
piece.left=slot.left
piece.board.update()
class janggiPiece(ft.GestureDetector):
def __init__(self, piece: janggibase.Piece, board: "janggiBoard"):
super().__init__()
self.piece=piece
self.board=board
self.mouse_cursor=ft.MouseCursor.MOVE
self.drag_interval=5
self.on_pan_start=start_drag
self.on_pan_update=drag
self.on_pan_end=drop
self.left = 60 * (piece.location // 10)
self.top = 60 * (9 - piece.location % 10)
self.content=ft.Container(
width=60,
height=60,
border_radius=ft.border_radius.all(5),
content=ft.Image(f"img/{str(piece).upper()}{piece.color}.png"),
)
class Slot(ft.Container):
def __init__(self, top, left):
super().__init__()
self.ontop = None
self.width = 60
self.height = 60
self.left = left
self.top = top
class janggiBoard(ft.Stack):
def __init__(self, board: janggibase.Board):
super().__init__()
self.board = board
self.slots = []
self.controls = []
self.width = 540
self.height = 600
for i in range(90):
self.slots.append(Slot(60 * (9 - i % 10), 60 * (i // 10)))
self.controls.extend(self.slots)
self.piecelist = []
for color in range(2):
for piece in self.board.pieces[color]:
self.piecelist.append(janggiPiece(piece, self))
self.controls.extend(self.piecelist)
self.move_start_top=None
self.move_start_left=None
if __name__ == "__main__":
def main(page):
page.add(janggiBoard(janggibase.Board(15)))
ft.app(target=main)
I also tried to switch the move_on_top function as below so that the piece never gets remoed from the controls, but I still got the same error.
def move_on_top(piece,controls:list):
i=controls.index(piece)
controls[i],controls[-1]=controls[-1],controls[i]
piece.board.update()
I had a looked at your code in the linked repo.
The main issue is that, in your helper methods you update the board
instead of updating the piece
. I made just the below 2 modifications to the code in your linked repo (janggiBoard.py
) and it worked again:
def bounce_back(board, piece):
# ...
piece.update()
def place(piece, slot):
# ...
piece.update()