Snippet Wednesday - My own TableView! 🌐

It is Snippet Wednesday again, and I thought I’d take the opportunity to share my personal little project to make app creation easier: my own TableView!, built on top of the versatile WebView. This snippet demonstrates how to use my TableView and shortly how it was built.

Snippet

For this “snippet”, I created a package that allows any VIKTOR developer to quickly convert a pandas dataframe to a visualization of tabular data, using my TableView. To install the package, you can simply add it to your requirements.txt file:

requirements.txt

viktor==14.4.0
viktor-table-view

Similar to any other view that is available in the VIKTOR SDK, you can add the TableView by adding it as a decorator on your method, and returning a TableResult. Here is an example of an implementation of the TableView:

app.py

import numpy as np
import pandas as pd
from viktor import ViktorController

from viktor_table_view import TableResult
from viktor_table_view import TableView


class SampleEntity(ViktorController):
    label = "Le Table"

    @TableView("le view", duration_guess=1)
    def le_view(self, **kwargs):
        # Generate a sample dataframe
        data = np.random.randn(20, 6)
        strings = np.array(["string1", "string2", "string3", "string4"])
        string_data = np.random.choice(strings, size=(20, 4))

        # Create the headers of this sample dataframe
        headers1 = ["Text"] * 4 + ["Number"] * 6
        headers2 = [f"Column {index + 1}" for index in range(0, 10)]

        # Combine the headers and data into a dataframe
        df = pd.DataFrame(np.column_stack([string_data, data]), columns=[headers1, headers2])
        df.iloc[:, 4:] = df.iloc[:, 4:].astype(float)

        # Now add colours to the sample dataframe by copying the dataframe and fill with info colours
        df_colours = df.copy(deep=True)
        df_colours.iloc[0, :] = "success"
        df_colours.iloc[1, :] = "warning"
        df_colours.iloc[2, :] = "error"
        df_colours.iloc[3:, :] = pd.NA
        return TableResult(df, dataframe_colours=df_colours, n_decimals={("Number", "Column 5"): 2})

Results

And voilà! You have created a table view that was custom built:

Current alternatives, and things to note…

I think this post would not be complete without mentioning the alternatives, and adding the things that one needs to consider.

Currently it is also possible to use packages such as bokeh and plotly to construct a table with a WebView or PlotlyView. There are some limitations, however, such as:

  • the syntax could be quite verbose if you simply want to convert a pandas DataFrame to html.
  • these packages can’t display multi-index columns or multi-index indices.

These points were addressed with this package, as well as:

  • customization can be done by for example use the n_decimals argument to determine by how many decimals you want to round your values, or by using the dataframe_colors argument to display the text in the colour of choice (e.g. “success”, “warning”, “error” or “info”).
  • advanced customization can also be passed on to the TableResult class by styling your pandas.Styler object yourself, and pass that to the style keyword argument.

How this was built

The TableView was built “on top of” the WebView. The WebView is a view that allows for any html to be rendered as a view. Therefore, you could render your own html if you want to. For my case, I created an html template, which was then used as basis for rendering the html that was passed on.

If you are curious to know more, let me know in the comments, or check out the repository, where you can look into the code of this custom TableView. Contributions to the view are very welcome.

Finally, I hope this triggers you to think of a view that you are currently missing. You can use the source code of this package as a basis to make your own VIKTOR view package!

12 Likes

Very nice package Sylvain, something I can imagine will be very valuable for many app developers!

This community package is also a nice environment to figure out how the ideal TableView should look like for the community. Is the signature easy-to-use? What kind of configuration options are necessary/desirable? Would be great if many developers would join the conversation and the package evolves.

If it has stabilized enough, we can consider moving it into the SDK to make installation easier

1 Like

Looks promising! I would like to see this added to the SDK in a near future.

So far, I have been using Table() from plotly.graph_objects but this TableView does indeed look better and simpler.

I think the visual presentation with grey borders looks good and guess it will please engineers used to looking at tables in Excel.

2 Likes

Thanks @Rasmus ! If you ever run into limitations, please add an issue here: Issues · viktor-platform/viktor_table_view · GitHub , or feel free to make a pull request!

Hope the TableView will make your life easier :slight_smile:

Just edited the post since it is now also installable using the following in requirements.txt:

viktor
viktor-table-view
2 Likes

@Rasmus: we are now planning to add a dedicated TableView in the SDK. You can track its progress here: Add TableView

UPDATE: it has been released in the meantime: Results & visualizations - Data & tables | VIKTOR Documentation

This is great. Though I hope there can be a method to justify the content. Right now it is all in the right side of the column. It would look nice if we can put it in the middle.

Hi @PanjiBrotoisworo !

If you would like to, you can already do so! :slight_smile:

From the example of the first post, if we would do the following:

styler = df.style.set_properties(**{"text-align": "center"})
return TableResult(df, dataframe_colours=df_colours, n_decimals={("Number", "Column 5"): 2}, style=styler)

It should give the following result:

You can of course experiment some more with the styling of the dataframe by reviewing how it works in the pandas documentation. Hope this helps!

1 Like

@Sylvain is table view supposed to max out at about 100 rows? or is this more of a user error?

Just to check, are you using @Sylvain own built TableView (which requires you to add it to your requirements, and import it from viktor_table_view), or are you using the VIKTOR SDK’s newly introduced TableView?

That is a GREAT question. My requirements.txt does include “viktor-table-view”, but based on your wording, i may be using the SDK TableView.

Below is the code in question:

@TableView("Subsurface Data: ", duration_guess=1)
def View_Soil_Table(self, params, **kwargs):

    combined_profile = self.compute_soil_profile(params, **kwargs)
    
    data = pd.DataFrame(combined_profile)
    data_headers=["Depth (ft)", "Soil Type",
                "Total Weight (pcf)", "Effective Weight (pcf)",
                "Total Stress (psf)", "Effective Stress", 
                "Undrained Shear Strength (psf)", "Angle of Friction (phi)", "Equivalent Shear Strength (psf)"]
    
    def highlight_granular(row):
        color = 'background-color: lightyellow' if row['Soil_Type'] == 'Granular' else ''
        return [color] * len(row)

    # Apply styling to highlight 'Granular' rows
    data = data.style.apply(highlight_granular, axis=1)

    return TableResult(data,
                       column_headers=data_headers)

Could you check where this is imported from?

Usually imports are at the top. If it is from the SDK, the import would look something like:

from viktor.views import TableView

Otherwise it would be:

from viktor_table_view import TableView

i call it from viktor.views and not viktor_table_view

from viktor.views import (DataGroup,
DataItem,
DataResult,
DataStatus,
DataView,
GeometryResult,
GeometryView,
ImageView,
ImageResult,
PDFView,
PDFResult,
PlotlyView,
PlotlyResult,
TableCell,
TableHeader,
TableView,
TableResult)

@mslootweg, what do you think considering the way it is imported?

Hi @gfsegre,

Thanks for reporting. There are actually 2 TableViews, you are currently using the ‘new’ TableView in the VIKTOR SDK. This one is released last July.
We are looking into the issue, we will keep you updated.

greetings,

Maarten