Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyParamGUI MVP #5

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

PyParamGUI MVP #5

wants to merge 10 commits into from

Conversation

anmolbhatia05
Copy link
Collaborator

@anmolbhatia05 anmolbhatia05 commented Jul 26, 2024

This MVP is made using -

  1. Anywidget for creating a widget and syncing the state between the ecmascript module frontend and python backend
  2. Pydantic for model schema and validation
  3. Pyglotaran for various IO utilities

Please provide enough information so that others can review your pull request:

Explain the details for making this change. What existing problem does the pull request solve?.

Change summary

  • Created MVP

Checklist

  • ✔️ Passing the tests (mandatory for all PR's)
  • 🚧 Added changes to changelog (mandatory for all PR's)
  • 👌 Closes issue (mandatory for ✨ feature and 🩹 bug fix PR's)
  • 🧪 Adds new tests for the feature (mandatory for ✨ feature and 🩹 bug fix PR's)
  • 📚 Adds documentation of the feature

Closes issues

closes #4

@jsnel
Copy link
Member

jsnel commented Jul 27, 2024

Hey @anmolbhatia05, while reviewing your PR, I'd suggest the following code changes:

👉 👌 Code Suggestion for #5

#5

♻️ Refactored code for better testability and readability 🧪

  • Refactored several functions to improve readability and maintainability
  • Replaced os module with pathlib for file operations
  • Improved docstrings for clarity and consistency and to keep ruff happy
  • Renamed 'widget' to '_widget' in widget.py to facilitate unit testing
  • Added a new test case for the _simulate function in widget.py

You can also review and apply these suggestions locally on your machine.

Learn more about GitKraken Code Suggest

Code Suggest liberates your code reviews from GitHub's restrictive, comment-only feedback style. As simple as suggesting changes in a Google-doc, provide real code suggestions from where you code, e.g. your IDE, and on anything in your project — not just on the lines of code changed in the PR.

Join your team on GitKraken to speed up PR review.

@jsnel
Copy link
Member

jsnel commented Jul 27, 2024

The above comment was (mostly) auto generated using GitKraken. I was trying out a new feature (code suggest) to generate and send code suggestions. 😄

You can watch a video about it over on YT.

jsnel and others added 4 commits July 27, 2024 14:51
- Refactored several functions to improve readability and maintainability
- Replaced os module with pathlib for file operations
- Improved docstrings for clarity and consistency and satify linters
- Renamed 'widget' to '_widget' in widget.py to facilitate unit testing
- Added a new test case for the _simulate function in widget.py
-
@anmolbhatia05 anmolbhatia05 requested a review from jsnel July 27, 2024 16:43
Copy link
Member

@s-weigand s-weigand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a minimum please remove the .DS_store and add it to the .gitignore.

THe following could also be done in a follow up PR/-s:

I think using anywidget isn't really needed for this project (it doesn't need custom functionality that can only be achieved by JS) and you will have it easier using existing panel widgets.
The advantage is that those widgets are already decently styled using google material design and they already have nice convenience functionality (e.g. for a FloatInput, you can define min, max values and step size).
Also, you can use python to do the layouting (e.g. Row and Column, as well as tabs and don't need to deal with the frontend side of things).
Also instead of always generating files I would just keep the strings/xr.Dataset in memory and only save on user request.
And finally I would combine multiple widgets into a logical group that returns your pydantic models as state.

Here is an example of a SpectralWidget with all needed inputs and a SpectralWidgetManager that allows adding and removing those (PoC).

import panel as pn
import param

pn.extension('dark')

# Define the SpectralWidget class
class SpectralWidget:
    def __init__(self, *, amplitude=0, location=0, width=1, skewness=0.1):
        # Create individual widgets
        self.amplitude_input = pn.widgets.FloatInput(name="Amplitude", value=amplitude, step=0.1)
        self.location_input = pn.widgets.FloatInput(name="Location", value=location, step=0.1)
        self.width_input = pn.widgets.FloatInput(name="Width", value=width, step=0.1)
        self.skewness_input = pn.widgets.FloatInput(name="Skewness", value=skewness, step=0.1)

        self.layout = pn.Row(
            self.amplitude_input, self.location_input, self.width_input, self.skewness_input
        )

    @property
    def amplitude(self):
        return self.amplitude_input.value

    @property
    def location(self):
        return self.location_input.value

    @property
    def width(self):
        return self.width_input.value

    @property
    def skewness(self):
        return self.skewness_input.value

    def view(self):
        return self.layout


# Create an instance of the custom class
spectral_widget = SpectralWidget()

# Display the combined widget
spectral_widget.view()
import panel as pn
import param
from pyparamgui.schema import SpectralParameters

# Define the SpectralWidgetManager class
class SpectralWidgetManager(param.Parameterized):
    
    def __init__(self, **params):
        super().__init__(**params)
        self.spectral_widgets = []
        self.widgets_container = pn.Column()
        
        # Create Add and Remove buttons
        self.add_button = pn.widgets.Button(name='Add SpectralWidget', button_type='primary')
        self.remove_button = pn.widgets.Button(name='Remove SpectralWidget', button_type='danger')
        
        # Link buttons to methods
        self.add_button.on_click(self.add_spectral_widget)
        self.remove_button.on_click(self.remove_spectral_widget)
        
        # Combine buttons and container into a Column
        self.layout = pn.Column(
            pn.Row(self.add_button, self.remove_button),
            self.widgets_container
        )
    
    def add_spectral_widget(self, event):
        new_widget = SpectralWidget()
        self.spectral_widgets.append(new_widget)
        self.widgets_container.append(new_widget.view())
    
    def remove_spectral_widget(self, event):
        if self.spectral_widgets:
            widget_to_remove = self.spectral_widgets.pop()
            self.widgets_container.remove(widget_to_remove.view())
            
    def get_state(self):
        state = SpectralParameters(amplitude=[],location=[],width=[],skewness=[])
        for spectral_widget in self.spectral_widgets:
            state.amplitude.append(spectral_widget.amplitude)
            state.location.append(spectral_widget.location)
            state.width.append(spectral_widget.width)
            state.skewness.append(spectral_widget.skewness)
        return state
        
        
    def view(self):
        return self.layout

# Create an instance of the manager class
spectral_widget_manager = SpectralWidgetManager()

# Display the manager widget
spectral_widget_manager.view()



@pytest.fixture()
def temp_dir():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pytest has a builtin tmp_path fixture that creates a temp folder per tests function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Mea culpa.

Initially I wanted to first have something I could easily run from main and fixtures don't play nice there, and then just copy pasted the code instead of switching to the dedicated fixture for that. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PyParamGUI MVP
3 participants