flexxui / flexx

Write desktop and web apps in pure Python
http://flexx.readthedocs.io
BSD 2-Clause "Simplified" License
3.27k stars 260 forks source link

JS: RangeError: Maximum call stack size exceeded #726

Open chinaericgithub opened 2 years ago

chinaericgithub commented 2 years ago

Hello,

The problem "JS: RangeError: Maximum call stack size exceeded" is often encountered in the project, which causes the project to click without continuing to respond.

error

Since I am not in-depth on the underlying code research, help analyze the reasons and solutions.

Thank you.

almarklein commented 2 years ago

From first glance, it looks like it is trying to encode an object tree that references itself. E.g. object A may be a list with a few objects, including object B, and Object B contains object A again.

I'm not really sure what the problem is unless I have the code to reproduce it. Would it be possible to create a small, minimal example that shows the error?

chinaericgithub commented 2 years ago

`from flexx import flx, ui

class Exampleui1(flx.Widget):

def init(self, demo):
    super().init()
    self.example1_label = flx.Label(text='my is example1')

class Example2itemui(flx.Widget):

def init(self, example2):
    super().init()
    self.example2 = example2
    with flx.HBox(css_class="item"):
        self.name = flx.Label(css_class="column name")

class Exampleui2(flx.Widget):

verify_tasks_data = flx.ListProp(settable=True)
pager_current = flx.IntProp(settable=True)

def init(self, example, example2):
    super().init()
    self.example = example
    self.example2 = example2
    self.example2_label = flx.Label(text='my is example2')
    self.btn_list_size_large = flx.Button(text="click list size large")

    #
    pager_current = 1
    if self.pager_current > 0:
        pager_current = self.pager_current
    #
    #
    with flx.HBox(css_class="verifybatchui_pager", style="position: relative;height:30px;"):
        self.pager_info = flx.Label(html="Total-Page, Current" + str(self.pager_current) + "Page",
                                        style="line-height:30px;")
        self.btn_first_pager = flx.Button(text='First')
        self.btn_previous_pager = flx.Button(text='Pre')
        self.btn_next_pager = flx.Button(text='Next')
        self.btn_last_pager = flx.Button(text='Last')

    with flx.Layout(style="overflow-y:scroll;height:400px;", css_class="verifybatchui_box") as self.verifybatch_task_box:
        self.verifybatch_task_tips = flx.Label(text='no data')

    # 更新数据
    self.example2.get_verify_tasks_data(self)
    #

@flx.reaction("btn_list_size_large.pointer_down")
def btn_list_size_large_click(self):
    self.example2.list_size_large()

@flx.action
def clean_verifybatch_task_box(self):
    self.verifybatch_task_box._mutate_children([])

@flx.reaction("verify_tasks_data")
def render_verify_tasks(self):
    self.clean_verifybatch_task_box()
    #
    pager = Pager()
    pager_current = 1
    if self.pager_current > 0:
        pager_current = self.pager_current
    #
    per_pager_num = pager.get_per_pager_num()
    #
    if len(self.verify_tasks_data) > 0:
        # content
        verify_tasks_data_slice_s = (pager_current - 1) * per_pager_num
        verify_tasks_data_slice_e = pager_current * per_pager_num
        verify_tasks_page_data = self.verify_tasks_data[verify_tasks_data_slice_s:verify_tasks_data_slice_e]
        for verify_task_item in verify_tasks_page_data:
            verifybatchitemui = Example2itemui(
                self.example2,
                parent=self.verifybatch_task_box,
                style="height:42px;"
            )
            verifybatchitemui.name.set_html(verify_task_item.get('name'))

    else:
        with flx.HBox(parent=self.verifybatch_task_box):
            self.nodata_tips = flx.Label(html='no data')  

    # pager
    total_pager = pager.get_total_pager(len(self.verify_tasks_data))
    #
    self.pager_info.set_html("Total" + str(total_pager) + "Page, Current" + str(pager_current) + "Page")

@flx.reaction("btn_next_pager.pointer_down")
def next_pager(self):
    # pager
    pager = Pager()
    total_pager = pager.get_total_pager(len(self.verify_tasks_data))
    #
    pager_current = 1
    if self.pager_current > 0:
        pager_current = self.pager_current
    if pager_current < total_pager:
        self.set_pager_current(pager_current + 1)
    else:
        self.set_pager_current(total_pager)
    self.example2.get_verify_tasks_data(self)

@flx.reaction("btn_previous_pager.pointer_down")
def previous_pager(self):
    pager_current = 1
    if self.pager_current > 1:
        pager_current = self.pager_current - 1
    self.set_pager_current(pager_current)
    self.example2.get_verify_tasks_data(self)

@flx.reaction("btn_first_pager.pointer_down")
def first_pager(self):
    self.set_pager_current(1)
    self.example2.get_verify_tasks_data(self)

@flx.reaction("btn_last_pager.pointer_down")
def last_pager(self):
    # pager
    pager = Pager()
    total_pager = pager.get_total_pager(len(self.verify_tasks_data))
    #
    self.set_pager_current(total_pager)
    self.example2.get_verify_tasks_data(self)

class Example2(flx.PyWidget):

@flx.action
def list_size_large(self):
    datasupply = Datasupply()
    datasupply.supply_data()

@flx.action
def get_verify_tasks_data(self, exampleui2):
    exampleui2.set_verify_tasks_data([])
    verify_tasks_data = []
    list = range(0, 5000)
    for i in list:
        verify_tasks_item = {'name':'name'+str(i)}
        verify_tasks_data.append(verify_tasks_item)
    exampleui2.set_verify_tasks_data(verify_tasks_data)

class Exampleui(flx.Widget): def init(self, example, example2): super().init() self.example = example self.example2 = example2

    self.btn_exampleui1 = flx.Button(text="Exampleui1")
    self.btn_exampleui2 = flx.Button(text="Exampleui2")

    with ui.StackLayout(flex=1) as self.verify_stack:
        self.btn_exampleui1.ui = Exampleui1()
        self.btn_exampleui2.ui = Exampleui2(self.example, self.example2)

@flx.reaction('btn_exampleui1.pointer_down',
              'btn_exampleui2.pointer_down'
              )
def _example_stacked_current(self, *events):
    button = events[-1].source
    self.verify_stack.set_current(button.ui)

class Example(flx.PyWidget):

def init(self):
    self.example2 = Example2()
    self.exampleui = Exampleui(self, self.example2, style="height:400px;")

class Datasupply:

def supply_data(self):
    export_data = []
    list = range(0, 5000)
    for i in list:
        export_item = [i, i]
        export_data.append(export_item)
    print(export_data)

class Pager:

per_pager_num = 10

def get_total_pager(self, total_item):
    total_pager =  total_item/self.per_pager_num
    total_pager_int = int(total_pager)
    if total_pager>total_pager_int:
        total_pager = total_pager_int +1
    return total_pager

def get_per_pager_num(self):
    return self.per_pager_num

if name == 'main': a = flx.App(Example) a.serve() a.launch('chrome-browser') # for use during development firefox,chrome-browser,chrome flx.start() `

chinaericgithub commented 2 years ago
20220520165525

Click several times, and then click the Last button, it is easy to report errors.

If you don't use StackLayout, it's fine. Because I don't have an in-depth understanding of the source code of Flexx, and I need to use StackLayout, so, help to see where the problem is? Or how to use StackLayout?

chinaericgithub commented 2 years ago

Hello @almarklein

If you have time, please help

Thanks

Konubinix commented 2 years ago

hao li @.***> writes:

If you have time, please help

I tried copy pasting your example, but has several indenting issues. I did not take the time to fix it.

IMHO, if you want to be helped quickly, attach the working example as a file, not as plain text.

-- Konubinix GPG Key : 7439106A Fingerprint: 5993 BE7A DA65 E2D9 06CE 5C36 75D2 3CED 7439 106A

chinaericgithub commented 2 years ago

demo.zip please see attachment @Konubinix @almarklein

chinaericgithub commented 2 years ago

I think the problem point

  1. StackLayout
  2. verifybatchitemui = Example2itemui( self.example2, parent=self.verifybatch_task_box, style="height:42px;" ) "when using parent".

When the above two are used together, this problem is prone to occur. This is what I observed.

chinaericgithub commented 2 years ago

Hello,

Found a problem? @Konubinix @almarklein I don't know how to solve the above problem. Looking forward to your reply

Konubinix commented 2 years ago

It goes way beyond my knowledge.

Although, I found a way to reduce the file to ease reproducing it. test.zip

I found out that putting the same code as Example2ui in Exampleui did not make the issue appear.

I used a tab layout to ease seeing no issue with the code inlined and the issue with the code of your example. image

Also, now, you only need to click on the button to see the issue.

I hope I did manage to keep showing the same issue as you had @chinaericgithub , but with a much smaller codebase and in a more repeatable way.

Also, @chinaericgithub , I suggest you read the guides of flexx, in particular https://flexx.readthedocs.io/en/stable/guide/patterns.html#use-of-a-central-data-store . I have the intuition that following those advices would not have put you in this situation in the first place. But this is only a wild guess.

chinaericgithub commented 2 years ago

Hi,

The problem is solved: I replaced class Exampleui(flx.Widget): with class Exampleui(flx.PyWidget).

I looked again at the documentation https://flexx.readthedocs.io/en/stable/guide/widgets_components.html and saw: A PyComponent always has a corresponding proxy object in JavaScript. A JsComponent may have a proxy object in Python; these proxy objects are created automatically when Python references the component.

I think TabLayout may have some problems with proxy conversion.