holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.83k stars 519 forks source link

Dataframe does not adjust height when changing from empty to non-empty #919

Closed MarcSkovMadsen closed 1 year ago

MarcSkovMadsen commented 4 years ago

ALL software version info

System Info

Description of expected behavior and the observed behavior

I'm building a small application where the user can upload a file and see a dataframe. The core of the app is

class TrainingAnalysisApp(param.Parameterized):
    """The Training Analysis App enables a user to analyze his training and performance"""

    training_file = param.FileSelector()
    training_data = param.DataFrame()

Initially the training_data is empty and shown as a line. I would have expected nothing to show, i.e. the line not showing.

image

After file upload the training_data header is shown but the height is so small that I cannot see the content. I would have expected the height to adjust to some number larger than 300px.

image

Furthermore I've tried but cannot set a fixed size for the training_data either. So I'm stuck not being able to show the DataFrame.

issue_dataframe

I've included a .fit file inside the .zip file below.

4394446039.zip

Complete, minimal, self-contained example code that reproduces the issue

You probably need to pip install fitparse to run the below.

"""The Training Analysis App provides functionality to

- upload a file
- view the route on an image
- see some basic metrics and charts
"""
import datetime
from typing import Dict, Tuple, Optional, List

import fitparse
import pandas as pd
import panel as pn
import param

class TrainingServices:
    """A Collection of services for working with training files and training data"""

    @classmethod
    def parse_fit_file(cls, file: bytes) -> pd.DataFrame:
        """Converts the bytes of a fit_file to a dataframe

        Args:
            file (bytes): The bytes of a fit file

        Raises:
            ValueError: If the file is not in a supported format

        Returns:
            pd.DataFrame: A DataFrame with the data
        """
        if isinstance(file, bytes):
            fit_file = fitparse.FitFile(file)
        else:
            raise ValueError(f"{type(file)} is not supported!")

        data = [record.get_values() for record in fit_file.get_messages("record")]
        return pd.DataFrame(data)

class TrainingAnalysisApp(param.Parameterized):
    """The Training Analysis App enables a user to analyze his training and performance"""

    training_file = param.FileSelector()
    training_data = param.DataFrame()

    @param.depends("training_file", watch=True)
    def parse_training_file(self):
        """Converts the training_file to the training_data"""
        self.training_data = TrainingServices.parse_fit_file(self.training_file)

    def view(self):
        """The main view of the TrainingAnalysisApp"""
        return pn.Column(
            pn.Param(
                self.param.training_file,
                widgets={"training_file": {"type": pn.widgets.FileInput, "accept": ".fit"}},
            ),
            self.param.training_data,
        )

def view():
    """Run this to run the application"""
    return TrainingAnalysisApp().view()

if __name__.startswith("bk"):
    view().servable()
MarcSkovMadsen commented 4 years ago

Was about to file an issue about this in another application, when I re-discovered I had already filed a similar one here.

MarcSkovMadsen commented 4 years ago

It's the same problem in a jupyter notebook.

image

image

MarcSkovMadsen commented 3 years ago

Workaround

I'm on Panel 0.12.1 and Bokeh 2.3.3

I can see that if you change

self.param.training_data,

to

pn.Param(self.param.training_data, widgets={"training_data": {"height": 400}})

It works

https://user-images.githubusercontent.com/42288570/132104086-a01a891d-9d5a-46cc-b5f6-834390cee0c5.mp4

"""The Training Analysis App provides functionality to

- upload a file
- view the route on an image
- see some basic metrics and charts
"""
import fitparse
import pandas as pd
import panel as pn
import param

class TrainingServices:
    """A Collection of services for working with training files and training data"""

    @classmethod
    def parse_fit_file(cls, file: bytes) -> pd.DataFrame:
        """Converts the bytes of a fit_file to a dataframe

        Args:
            file (bytes): The bytes of a fit file

        Raises:
            ValueError: If the file is not in a supported format

        Returns:
            pd.DataFrame: A DataFrame with the data
        """
        if isinstance(file, bytes):
            fit_file = fitparse.FitFile(file)
        else:
            raise ValueError(f"{type(file)} is not supported!")

        data = [record.get_values() for record in fit_file.get_messages("record")]
        print(data)
        return pd.DataFrame(data)

class TrainingAnalysisApp(param.Parameterized):
    """The Training Analysis App enables a user to analyze his training and performance"""

    training_file = param.FileSelector()
    training_data = param.DataFrame(pd.DataFrame())

    @param.depends("training_file", watch=True)
    def parse_training_file(self):
        """Converts the training_file to the training_data"""
        self.training_data = TrainingServices.parse_fit_file(self.training_file)

    def view(self):
        """The main view of the TrainingAnalysisApp"""
        return pn.Column(
            pn.Param(
                self.param.training_file,
                widgets={"training_file": {"type": pn.widgets.FileInput, "accept": ".fit"}},
            ),
            pn.Param(self.param.training_data, widgets={"training_data": {"height": 400}}),
        )

def view():
    """Run this to run the application"""
    return TrainingAnalysisApp().view()

if __name__.startswith("bokeh"):
    view().servable()

You can use this file if you rename it from .csv to .fit.

SpeedCoach GPS JustGo.csv

MarcSkovMadsen commented 3 years ago

Solution

My suggestion would be to describe in the panel.widgets.DataFrame reference guide that if you start out with an empty dataframe you will need to give the panel.widgets.DataFrame a fixed height. If not it will use a minimal height and not expand later.

I have struggled a lot with this over time. So I think its worth the effort to describe the issue and solution.

philippjfr commented 1 year ago

Appears to be resolved on main.