zauberzeug / nicegui

Create web-based user interfaces with Python. The nice way.
https://nicegui.io
MIT License
9.79k stars 581 forks source link

Log viewer inside tabs issue #1869

Closed marcin-koziol closed 7 months ago

marcin-koziol commented 1 year ago

Description

I want to place the Log component in the tab, everything is OK, but to see the logs, you first need to switch to the Log viewer tab

from datetime import datetime
from nicegui import ui

with ui.row():
    with ui.tabs().classes('w-full').props(add="dense align=left") as tabs:
        one = ui.tab('Something')
        logs = ui.tab('Logs')
    ui.separator()

    with ui.tab_panels(tabs, value=one).classes('w-full').style('min-height: 268px'):
        with ui.tab_panel(one):
            ui.image('https://picsum.photos/id/377/640/360').style('max-width: 420px')
        with ui.tab_panel(logs):
            log = ui.log(max_lines=10).classes('w-full h-20')

with ui.row():
    ui.button('Log time', on_click=lambda: log.push(datetime.now().strftime('%X.%f')[:-5]))

ui.run()

In this example:

falkoschindler commented 1 year ago

Wow, thanks for reporting this issue, @marcin-koziol!

It seems like the log element doesn't exist before opening the tab. When I print its ID print(log.id) (→"13") and try to get the element from the JavaScript console getElement(13), it is undefined.

Right now I'm not sure how to fix it. When calling log.update() right after pushing new content, it works. But the main idea of ui.log was to not update the whole element for each new line. I guess there's a better way...

marcin-koziol commented 12 months ago

maybe we could add the ability to bind a variable to the log (same as for inputs), so we'll be able to push logs to this var , without having rendered component? Is the truncating logic implemented only on the client?

falkoschindler commented 12 months ago

Yes, the truncating logic is implemented on the client. But more importantly, we don't want to send the whole log of maybe thousands of lines to the client every time a new line is pushed. Instead we only want to send an incremental update. Therefore I'd like to avoid any fix or workaround involving log.update(), which would be called internally if would bind its content to some source.

falkoschindler commented 11 months ago

I still have no idea how to fix this issue. Therefore I'll remove the milestone for now.

falkoschindler commented 10 months ago

We have spotted a similar problem with AG Grids:

with ui.tabs().classes('w-full') as tabs:
    one = ui.tab('One')
    two = ui.tab('Two')
with ui.tab_panels(tabs, value=one).classes('w-full'):
    with ui.tab_panel(one):
        async def get_data():
            ui.notify(await grid.get_client_data())
        ui.button('Get data', on_click=get_data)
    with ui.tab_panel(two):
        grid = ui.aggrid({
            'columnDefs': [{'headerName': 'Name', 'field': 'name'}],
            'rowData': [{'name': 'Alice'}, {'name': 'Bob'}],
        })

Like with ui.log the UI element doesn't get mounted until the user visits the tab. How can we fix that?

E-H-E-H commented 7 months ago

Hi, just wondering, what is the best strategy for an workaround? , if you want multiple logs updating on multiple views? (for example on seperate pages, on multiple tabs, etc.)

falkoschindler commented 7 months ago

@E-H-E-H It shouldn't be a problem on separate pages. For the issue with ui.log inside hidden tabs I don't have any new ideas or workarounds...

But wait! Maybe we can re-implement ui.log with a much simpler approach. By simply adding labels to a scroll view, we can achieve a similar behavior which even works on hidden tabs:

import time
from typing import Optional
from nicegui import ui

ui.add_head_html('<style>.custom-log .q-scrollarea__content { padding: 0.25rem; gap: 0; }</style>')

class Log(ui.scroll_area):

    def __init__(self, max_lines: Optional[int] = None) -> None:
        super().__init__()
        self.classes('custom-log font-mono whitespace-pre-wrap bg-gray-50 border border-gray-200')
        self.max_lines = max_lines

    def push(self, text: str) -> None:
        with self:
            ui.label(text)
        while self.max_lines is not None and len(self.default_slot.children) > self.max_lines:
            self.remove(0)
        self.scroll_to(percent=100)

log = Log(max_lines=20)
log.push('Hello, world!')
log.push('Hello, world!')
E-H-E-H commented 7 months ago

@falkoschindler,

I see that you made a new implementation of the ui.log(). Great! thanks a lot! :)

kyloe commented 5 months ago

I have just run into the AG Grid issue - I have multiple grids on multiple tabs. Is there any similar work around now (or in the pipeline) for grids?

falkoschindler commented 5 months ago

@kyloe Unfortunately, the problem with AG Grids isn't solved yet, and we're still looking for a workaround or solution. I just created issue #3033 to track it and to ask the community for help.