@@ -142,78 +142,77 @@ Finally, we connect a UI component to the connector object. The template applica
InputField(v_model="config.username")
```
## Implementing MVVM with `nova-mvvm` and Pydantic
## Project Structure
Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate Pydantic for data validation.
The template creates a well-organized project structure following best practices, including the Model-View-ViewModel (MVVM) design pattern. This structure promotes code maintainability, testability, and separation of concerns. Here's a breakdown of the key directories and files:
**1. Adding Fractal to the ViewModel (`src/nova_tutorial/app/view_models/main.py`) (Modify):**
*`nova_tutorial/`: The root directory of your project. This is the top-level directory containing all project files and subdirectories.
***Running our Model**: We start by adding a method to bottom of our ViewModel which will run the Fractal tool.
*`nova_tutorial/src/`: This directory contains all the source code for your application. The separation into `src` helps distinguish your application code from configuration files, tests, and other project-related files that reside in the root.
```python
defrun_fractal(self)->None:
self.model.fractal.run_fractal_tool()
self.update_view()
```
*`nova_tutorial/src/nova_tutorial/`: This is the main Python package for your application. Its name (`nova_tutorial` in this case) is used when importing modules within your project. Inside this directory, you'll find the core application logic, organized according to the MVVM pattern:
**2. Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py`) (Modify)**
* `nova_tutorial/src/nova_tutorial/app/`: This directory contains the main application logic, further subdivided to reflect the MVVM structure.
***Adding new imports**: We need to add some imports for pydantic and working with base64 encodings to deal with the image. Modify your import block to match below.
* `nova_tutorial/src/nova_tutorial/app/models/`: **(Model)** This is where you define your data models and business logic. These classes represent the data your application works with and the rules for manipulating that data.
```python
importos
frombase64importb64encode
fromtypingimportLiteral
* `nova_tutorial/src/nova_tutorial/app/view_models/`: **(ViewModel)** This directory holds the ViewModels. These classes act as intermediaries between the Models and the Views. They prepare data for display and handle user interactions from the View.
frompydanticimportBaseModel,Field
fromnova.galaxyimportConnection,Parameters,Tool
```
* `nova_tutorial/src/nova_tutorial/app/views/`: **(View)** This directory contains the user interface (UI) components. These are built using Trame and Vuetify (via `nova-trame`). They are responsible for displaying data and capturing user input.
***Update class variables:** Now we will update fractal_type and other class variables to support pydantic. We will also add an image variable to store the image. Modify the variable declarations to the following:
* `nova_tutorial/src/nova_tutorial/app/main.py`: The entry point for your NOVA application. This file initializes and starts the Trame server and the `MainApp` view.
*`nova_tutorial/tests/`: Contains unit tests for your application. A well-structured project should include tests to ensure code quality and prevent regressions. The tests are typically organized to mirror the structure of your application code (e.g., tests for models, view models, and potentially UI components).
defset_fractal_type(self,fractal_type:str):
self.fractal_type=fractal_type
```
*`nova_tutorial/README.md`: A Markdown file providing a description of your project, instructions for setup and usage, and any other relevant information.
***Decode the image data:** Finally, we need to decode the image that we receive as the output from the tool execution. Modify the section where we execute the tool to the following:
*`pyproject.toml`: A configuration file for Poetry, the dependency management and packaging tool used by NOVA. It specifies project dependencies, build settings, and other metadata.
```python
output.get_dataset("output").download("tmp.png")
## Implementing MVVM with `nova-mvvm` and Pydantic
Let's implement the MVVM pattern, starting with the UI and basic ViewModel connections, and then building up the Model functionality.
**1. Initial Setup and ViewModel Basics**
First, let's simplify our `main.py` and set up the basic structure of our UI and ViewModel interaction. We'll create a button in the UI that, when clicked, will eventually run our Fractal tool. For now, it will just trigger a placeholder method in the ViewModel.
**3. Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py`) (Modify):**
***`main.py` - Simplifying the Application Entry Point (`src/nova_tutorial/app/main.py`) (Modify):**
***Add Fractal to imports**: Add an import for the Fractal class into our MainModel.
We're removing the direct Fractal tool execution from `main()`. The application will now solely focus on launching the NOVA app.
```python
from.fractalimportFractal# Import Fractal
importsys
defmain()->None:
kwargs={}
from.views.mainimportMainApp
app=MainApp()
forarginsys.argv[2:]:
try:
key,value=arg.split("=")
kwargs[key]=int(value)
exceptException:
pass
app.server.start(**kwargs)
```
***Add the Fractal Model to the MainModel**: Modify the end of the MainModel class so that it matches the code below.
**5. Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`) (Modify):**
* **Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`) (Modify):**
***Add Fractal Tab to the tab panel**: Modify the tab panel to add our new Fractal tab
Add the "Fractal" tab to the tab bar.
```python
with vuetify.VTabs(v_model=("active_tab", 0), classes="pl-5"):
@@ -241,17 +239,14 @@ class FractalTab:
vuetify.VTab("Sample Tab 2", value=3)
```
**6. Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`) (Modify):**
* **Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`) (Modify):**
***Add FractalTab to imports**: Import the newly created FractalTab class into our tab_content_panel.
Display the `FractalTab` content when the "Fractal" tab is selected.
```python
from .fractal_tab import FractalTab # Import the FractalTab
```
***Add the Fractal Tab to our existing tabs**: Add the Fractal Tab lines to the vuetify.VWindow section and modify the values.
```python
# ... (rest of the file) ...
with vuetify.VWindow(v_model="active_tab"):
with vuetify.VWindowItem(value=1):
FractalTab(self.view_model) # Add FractalTab
@@ -261,40 +256,95 @@ from .fractal_tab import FractalTab # Import the FractalTab
SampleTab2()
```
**7. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`) (Modify):**
**Demonstration (Initial UI and ViewModel Connection):**
Run the application: `poetry run app`
You should see a new "Fractal" tab in the application. Click the "Run Fractal" button. You should see "run_fractal method called!" printed in your terminal. This demonstrates that the button click in the View is successfully triggering the `run_fractal` method in the ViewModel, even though the method doesn'tdoanythingsubstantialyet.ThisestablishesthebasicMVVMwiring.
**2.FractalModelandPydanticIntegration**
We are now going to modify the existing `main.py` file. Change the main method to match the code below.
Now,let's build out the `Fractal` model using Pydantic and integrate it into our `MainModel`.
***Instantiate and Run**: In the `main()` function, we no longer need to setup the Fractal tool as it's managed via our MVVM application now.
* **Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py`) (Modify)**
* **Adding new imports**: Add imports for Pydantic and base64 handling.
```python
importsys
from.models.fractalimportFractal
import os
from base64 import b64encode
from typing import Literal
from pydantic import BaseModel, Field
from nova.galaxy import Connection, Parameters, Tool
```
defmain()->None:
kwargs={}
from.views.mainimportMainApp
***Update class variables:** Use Pydantic's `Field` for type hinting and validation. Add the `image_data` field.
fractal:Fractal=Field(default_factory=Fractal)#Add Fractal Model
```
The application should launch a tab in your web browser. The GUI will have a `FRACTAL` tab and a few sample tabs which were created by the template application. The run button on the `Fractal` tab can be used to launch the `Fractal` NDIP tool. The tool will take a few minutes to complete but when it does, the resulting `Fractal` image will be displayed.
***Connect the UI elements in FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`) (Modify):**
Update the create UI section to use InputField and the image.
***Add Full Functionality to the View Model (`src/nova_tutorial/app/view_models/main.py`) (Modify)**
Update the code in the run_fractal method.
```python
defrun_fractal(self)->None:
self.model.fractal.run_fractal_tool()
self.update_view()
```
**Final Demonstration (Full Application):**
Run the application: `poetry run app`
Now, when you click "Run Fractal," the Fractal tool will execute in Galaxy, and the resulting image will be displayed in the UI. You can also change the `fractal_type` using the input field. This demonstrates the complete MVVM flow, with data binding, Pydantic validation, and the interaction between the View, ViewModel, and Model.
This revised structure breaks down the implementation into smaller, more manageable steps, with demonstrations after each stage to show the progress and confirm that each part is working as expected. This addresses the feedback about making too many code changes at once and improves the learning experience.
::::::::::::::::::::::::::::::::::::::::: callout
If you don't want Trame to launch a tab by default, you can instead run ```poetry run app --server```.
If you want to connect your application to the analysis cluster, then it will need to be run on a computer where the filesystem is mounted. If your application is deployed through our platform, then we can ensure that your application runs in the correct environment to support your needs.
@@ -268,7 +291,7 @@ By combining these layout components, you can create complex and responsive UI l
As an example, we can use the layout classes to center the "Run Fractal" button.