首页 > 编程问答 >如何解决“必须首先将控件添加到页面”的问题。机队错误?

如何解决“必须首先将控件添加到页面”的问题。机队错误?

时间:2024-07-26 16:02:32浏览次数:8  
标签:python python-3.x flet

我正在尝试使用 flet 制作 janggi(国际象棋变体)游戏。由于我必须在多个地方使用该板,因此我创建了一个名为 janggiBoard 的类,它继承自 ft.Stack。我想对片段使用拖放操作,因此我按照 flet 文档中的 Solitaire 教程进行操作。当我尝试执行“move_on_top”部分时出现问题。在Solitaire代码中,move_on_top函数中有 page.update() 所以我很自然地在后面加上了 piece.board.update() 但是,当我这样做时,它会导致如下所示的断言错误。

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 25, in start_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.

当我搜索此错误时,他们说当您在未将控件添加到页面时调用更新时会发生这种情况。但是,该板显然已添加到页面中,因此我不明白为什么会发生这种情况。

import flet as ft
from janggiplayer import AI
import flet.canvas as cv
import janggibase
from db import db

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
    if e.control.piece.color == e.control.board.board.turn and (
            not e.control.board.ai or e.control.piece.color != e.control.board.aiturn
        ):
            for slot in e.control.board.slots:
                if e.control.piece.isValidMove(e.control.board.slots.index(slot)):
                    slot.content = ft.Image(f"img/able.png")
                    e.control.board.controls.remove(slot)
                    e.control.board.controls.append(slot)
                    e.control.update()
                    slot.update()


def bounce_back(board,piece):
    piece.top=board.move_start_top
    piece.left=board.move_start_left
    piece.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:
        slot.content=None
        slot.update()

    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.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.width = 60
        self.height = 60
        self.left = left
        self.top = top


class janggiBoard(ft.Stack):
    def __init__(self, board: janggibase.Board, ai=False, aiturn=None):
        super().__init__()
        
        self.board = board
        self.slots = []
        self.controls = []
        self.width = 540
        self.height = 600
        self.piecelist = []
        self.move_start_top=None
        self.move_start_left=None
        self.ai = AI(self.board, aiturn) if ai else None
        self.aiturn = aiturn
    def did_mount(self):
        cp = cv.Canvas(
            [
                cv.Path(
                    [
                        cv.Path.MoveTo(30, 30),
                        cv.Path.LineTo(30, 570),
                        cv.Path.MoveTo(90, 30),
                        cv.Path.LineTo(90, 570),
                        cv.Path.MoveTo(150, 30),
                        cv.Path.LineTo(150, 570),
                        cv.Path.MoveTo(210, 30),
                        cv.Path.LineTo(210, 570),
                        cv.Path.MoveTo(270, 30),
                        cv.Path.LineTo(270, 570),
                        cv.Path.MoveTo(330, 30),
                        cv.Path.LineTo(330, 570),
                        cv.Path.MoveTo(390, 30),
                        cv.Path.LineTo(390, 570),
                        cv.Path.MoveTo(450, 30),
                        cv.Path.LineTo(450, 570),
                        cv.Path.MoveTo(510, 30),
                        cv.Path.LineTo(510, 570),
                        cv.Path.MoveTo(30, 30),
                        cv.Path.LineTo(510, 30),
                        cv.Path.MoveTo(30, 90),
                        cv.Path.LineTo(510, 90),
                        cv.Path.MoveTo(30, 150),
                        cv.Path.LineTo(510, 150),
                        cv.Path.MoveTo(30, 210),
                        cv.Path.LineTo(510, 210),
                        cv.Path.MoveTo(30, 270),
                        cv.Path.LineTo(510, 270),
                        cv.Path.MoveTo(30, 330),
                        cv.Path.LineTo(510, 330),
                        cv.Path.MoveTo(30, 390),
                        cv.Path.LineTo(510, 390),
                        cv.Path.MoveTo(30, 450),
                        cv.Path.LineTo(510, 450),
                        cv.Path.MoveTo(30, 510),
                        cv.Path.LineTo(510, 510),
                        cv.Path.MoveTo(30, 570),
                        cv.Path.LineTo(510, 570),
                        cv.Path.MoveTo(210, 30),
                        cv.Path.LineTo(330, 150),
                        cv.Path.MoveTo(330, 30),
                        cv.Path.LineTo(210, 150),
                        cv.Path.MoveTo(210, 450),
                        cv.Path.LineTo(330, 570),
                        cv.Path.MoveTo(330, 450),
                        cv.Path.LineTo(210, 570),
                    ],
                    paint=ft.Paint(
                        color="white",
                        stroke_width=2,
                        style=ft.PaintingStyle.STROKE,
                    ),
                ),
            ],
            width=float("inf"),
            expand=True,
        )
        self.controls.append(cp)
        for i in range(90):
            self.slots.append(Slot(60 * (9 - i % 10), 60 * (i // 10)))
        self.controls.extend(self.slots)
        for color in range(2):
            for piece in self.board.pieces[color]:
                self.piecelist.append(janggiPiece(piece, self))
        self.controls.extend(self.piecelist)
        self.update()
        return super().did_mount()
    

if __name__ == "__main__":

    def main(page):
        page.add(janggiBoard(janggibase.Board(15)))

    ft.app(target=main)

这是该板的代码。完整的代码在我的 github 上。

在我评论了 piece.board.update() 之后,其他一切都按预期工作。我可以将棋子移动到不同的位置。然而,由于主板未更新,该作品并未位于其他作品之上。有没有办法更新主板而不导致错误?


这个错误是由于 Flet 框架的工作原理造成的。当你调用 piece.board.update() 的时候, piece.board (也就是你的 janggiBoard 实例) 还没有被正式添加到页面中。尽管你已经在 main 函数中调用了 page.add(janggiBoard(...)) ,但这只是将控件加入到页面的更新队列中,而实际的页面更新是异步进行的。这意味着 piece.board.update() 的执行时机可能早于 janggiBoard 真正被添加到页面,所以就导致了这个错误。

解决这个问题的方法是确保 piece.board.update() janggiBoard 被添加到页面之后才被调用。你可以利用 Flet 提供的 page.after_render 回调函数来实现:

def move_on_top(piece,controls,page):
    controls.remove(piece)
    controls.append(piece)
    # 使用 page.after_render 确保更新在渲染完成后进行
    def update_board():
        piece.board.update()
    page.after_render(update_board)

def start_drag(e:ft.DragStartEvent):
    move_on_top(e.control,e.control.board.controls,e.page)
    # ... 其他代码 ...

通过将 piece.board.update() 包裹在 page.after_render 回调函数中,你可以确保它会在页面渲染完成后才被执行,从而避免 "Control must be added to the page first" 的错误。

另外,你也可以尝试使用 async await 来实现异步操作,这样可以更加优雅地处理页面更新问题。但是,这需要对 Flet 框架的异步机制有更深入的了解。

标签:python,python-3.x,flet
From: 78796859

相关文章

  • 在 Python 中将 Kivy 文件选择器添加到 PopUp
    我一直在尝试通过应用程序的按钮释放创建文件选择器弹出窗口。我分别管理了FileChooser和Popups,但无法将两者一起解决,这里有人可以帮助解决问题吗?我正在尝试用Python而不是Kivy.lang来实现PopUp,因为这是我在弹出窗口方面的经验。我也无法让KivyDoc示例正常工作。我......
  • Python基础知识点(1)基本语句
    基本语句1.if语句if表达式:语句块其中,表达式是一个返回True或False的表达式。如果表达式为True,则执行if下面的语句块;如果为False,则跳过语句块执行下面的语句。2.if…else语句if表达式:语句块1else:语句块2其中,表达式是一个返回True或False的表达式。如果......
  • 使用Python实现深度学习模型:语言翻译与多语种处理
    引言语言翻译和多语种处理是自然语言处理(NLP)中的重要任务,广泛应用于跨语言交流、国际化应用和多语言内容管理等领域。通过使用Python和深度学习技术,我们可以构建一个简单的语言翻译与多语种处理系统。本文将介绍如何使用Python实现这些功能,并提供详细的代码示例。所需工具......
  • python框架之Flask
    之前写过有关flask-restful: https://www.cnblogs.com/xingxia/p/flask_restful.html虽然早期使用python进行web应用搭建的使用该框架,但是好像很少总结,在此记录一下 [安装]pip3installflask [使用]#导入类库fromflaskimportFlask#创建实例......
  • Python 搜索和抓取
    我有一个问题想知道是否值得花时间尝试用Python来解决。我有一个包含鱼类学名的大型CSV文件。我想将该CSV文件与大型鱼类形态信息数据库(www.fishbase.ca)交叉引用,并让代码返回每条鱼的最大长度。基本上,我需要创建代码来搜索Fishbase网站上的每条鱼,然后找到页面上的最......
  • 《最新出炉》系列入门篇-Python+Playwright自动化测试-54- 上传文件(input控件) - 上篇
    1.简介在实际工作中,我们进行web自动化的时候,文件上传是很常见的操作,例如上传用户头像,上传身份证信息等。所以宏哥打算按上传文件的分类对其进行一下讲解和分享。2.上传文件的API(input控件)Playwright是一个现代化的自动化测试工具,它支持多种浏览器和操作系统,可以帮助开发人员和......
  • python requests 报错 Caused by ProxyError ('Unable to connect to proxy', OSError
    背景:访问https接口,使用http代理版本:requests:2.31.0 从报错可以看出,是proxy相关的报错调整代码,设定不使用代理,将http与https对应的proxy值置空即可(尝试过proxies={},但此写法不生效)proxies={'http':'','https':''}response = requests.get('https://xxx......
  • python基础函数
    1.为什么使用函数使用函数的目的是去减少代码的冗余性,简化代码的复杂度2.如何去定义一个函数以def开头去进行相关的定义在def的后面我们就去以见明知意的方式去定义一个函数的名称在函数名称后面的括号中去添加参数值,可以是多个参数,也可以是无餐的3.函数的调用无参多......
  • 不使用 + 或 - 运算符 | 添加 2 个数字Python
    我一直在尝试编写逻辑,但测试用例失败。如何改进我的代码?代码:#Giventwointegersaandb,returnthesumofthetwointegerswithoutusingtheoperators+and-.a=-1b=1min_val=min(a,b)max_val=max(a,b)ifmin_val==max_val:pr......
  • python 中的智能 Cisco IOS 差异
    之前:hostnameFoo!interfaceGigabitEthernet1/1switchportmodetrunkswitchporttrunkallowedvlan10,20,30!interfaceGigabitEthernet1/2ipaddress192.0.2.1255.255.255.128noipproxy-arp!之后:hostnameFoo!interfaceGigabitEt......