Skip to content

Commit

Permalink
[TUI] Add validation to create folders (#276)
Browse files Browse the repository at this point in the history
* Add validation to subject and session Inputs.

* Load datatype checkbox status from a persistent settings file stored in project config folder.

* Introduce CreateFolderSettings page to manage the template names.

* Format and give functionality to the template settings page.

* Add hyperlink, small tidy ups.

* Tidy up css and add light-mode for new widgets.

* Refactoring and tidy ups on TUI modules.

* Handle nonetype templates properly.

* Small refactorings and tidy-ups.

* Align + tidy up CreateFoldersTab button widgets

* Minor cleanups `tui_menu.tcss`

* Update project selector menubar to allow vertical scroll bar.

* Update datatype dict on mount so that any persistent settings are reflected in `datatype_dict`.

* Some more minor cleanup

---------

Co-authored-by: b-peri <[email protected]>
  • Loading branch information
JoeZiminski and b-peri committed Feb 1, 2024
1 parent 9f187df commit 9542421
Show file tree
Hide file tree
Showing 8 changed files with 622 additions and 40 deletions.
14 changes: 9 additions & 5 deletions datashuttle/tui/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from datashuttle import DataShuttle
from datashuttle.tui.screens import modal_dialogs
from datashuttle.tui.utils import utils
from datashuttle.tui.utils import tui_utils


class ConfigsContent(Container):
Expand Down Expand Up @@ -161,7 +161,9 @@ def on_radio_set_changed(self, event: RadioSet.Changed) -> None:
Update the displayed SSH widgets when the `connection_method`
radiobuttons are changed.
"""
display_bool = True if str(event.pressed.label) == "SSH" else False
label = str(event.pressed.label)
assert label in ["SSH", "Local Filesystem"], "Unexpected label."
display_bool = True if label == "SSH" else False
self.switch_ssh_widgets_display(display_bool)

def switch_ssh_widgets_display(self, display_bool):
Expand Down Expand Up @@ -214,7 +216,9 @@ def setup_configs_for_a_new_project_and_switch_to_tab_screen(self):

self.parent_class.mainwindow.push_screen(
modal_dialogs.ShowConfigsDialog(
utils.get_textual_compatible_project_configs(project.cfg),
tui_utils.get_textual_compatible_project_configs(
project.cfg
),
"A DataShuttle project with the below "
"configs has now been created.\n\n Click 'OK' to proceed to "
"the project page, where you will \n be able to create and "
Expand All @@ -238,7 +242,7 @@ def setup_configs_for_an_existing_project(self):
try:
self.project.make_config_file(**cfg_kwargs)

configs_to_show = utils.get_textual_compatible_project_configs(
configs_to_show = tui_utils.get_textual_compatible_project_configs(
self.project.cfg
)

Expand All @@ -262,7 +266,7 @@ def fill_widgets_with_project_configs(self):
"ssh" widgets are hidden / displayed based on the current setting,
in `self.switch_ssh_widgets_display()`.
"""
cfg_to_load = utils.get_textual_compatible_project_configs(
cfg_to_load = tui_utils.get_textual_compatible_project_configs(
self.project.cfg
)

Expand Down
87 changes: 86 additions & 1 deletion datashuttle/tui/css/tui_menu.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
align: center top;
margin: 3 2 0 0;
content-align: center middle;
height: auto;
}

#project_select_top_container > Button {
Expand Down Expand Up @@ -146,7 +147,6 @@ ShowConfigsDialog:light > #display_configs_top_container {

#configs_local_path_label {
align: center middle;
padding: 0 0 0 0;
}

#configs_container > Input {
Expand Down Expand Up @@ -199,3 +199,88 @@ config label and button align center */
height: auto;
align: center middle;
}


/* TemplateSettingsScreen --------------------------------------------------------------- */

TemplateSettingsScreen {
align: center middle;
}

#template_top_container {
height: 25;
width: 80;
background: $primary-background;
border: tall $panel-lighten-3;
}

#template_inner_horizontal_container{
height: 3;
}

#template_inner_container {
height: auto;
overflow: auto hidden;
}

#template_message_label {
width: auto;
margin: 1 0 0 2;
}

#template_settings_radioset {
layout: horizontal;
border: none;
background: transparent;
align: center middle;
width: 50%;
margin: 1 0 0 0;
}

#template_settings_input {
align: center middle;
width: 50%;
}

#template_sessions_close_button{
dock: right;
layer: top;
}

#template_other_widgets_container{
align: center top;
content-align: center top;
}

#template_message_label:disabled {
color: $panel-lighten-3;
link-color: $panel-lighten-3;
}

#template_settings_radioset:disabled {
color: $panel-lighten-3;
}

#template_settings_input:disabled {
color: $panel-lighten-3;
}

/* TemplateSettingsScreen Light Mode */

#template_top_container:light {
border: tall $panel-darken-3;
background: $boost;
}

#template_settings_radioset:light:disabled {
color: $panel-darken-2;
}

#template_message_label:light:disabled {
color: $panel-darken-2;
link-color: $panel-darken-2;
}

#template_settings_input:light:disabled {
color: $panel-darken-2;
}
35 changes: 35 additions & 0 deletions datashuttle/tui/css/tui_tab.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,38 @@ TabScreen > TabbedContent {
width: auto;
padding: 0 0 1 0;
}

CreateFoldersTab > Horizontal > Button{
width: 30%;
}

CreateFoldersTab > Horizontal{
align-horizontal: left;
}

#tabscreen_create_tab > Input.-valid {
border: tall $success 60%;
}
#tabscreen_create_tab > Input.-valid:focus {
border: tall $success;
}

#tabscreen_create_tab > Input.-invalid {
border: tall $error 60%;
}

#tabscreen_create_tab > Input.-invalid:focus {
border: tall $error
}

#template_settings_validation_on_checkbox.-on > .toggle--button{
color: $success;
}

#template_settings_validation_on_checkbox.-on > .toggle--label{
color: $success;
}

#template_settings_validation_on_checkbox > .toggle--button{
color: $error;
}
89 changes: 76 additions & 13 deletions datashuttle/tui/custom_widgets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Dict

if TYPE_CHECKING:
from textual import events

from dataclasses import dataclass

from textual.message import Message
from textual.reactive import reactive
from textual.widgets import Checkbox, Static
from textual.widgets import Checkbox, Input, Static

from datashuttle.configs.canonical_configs import get_datatypes

Expand All @@ -12,38 +22,91 @@ class DatatypeCheckboxes(Static):
Attributes
----------
type_out:
datatype_out:
List of datatypes selected by the user to be passed to `make_folders`
(e.g. "behav" that will be passed to `make-folders`.)
type_config:
List of datatypes supported by NeuroBlueprint
"""

type_out = reactive("all")
datatype_out = reactive("all")

def __init__(self):
def __init__(self, project):
super(DatatypeCheckboxes, self).__init__()

self.type_config = get_datatypes()
self.project = project
self.datatype_config = get_datatypes()
self.persistent_settings = self.project._load_persistent_settings()

def compose(self):
for type in self.type_config:
checkboxes_on = self.persistent_settings["tui"]["checkboxes_on"]

for datatype in self.datatype_config:
assert datatype in checkboxes_on.keys(), (
"Need to update tui" "persistent settings."
)

yield Checkbox(
type.title(), id=f"tabscreen_{type}_checkbox", value=True
datatype.title(),
id=f"tabscreen_{datatype}_checkbox",
value=checkboxes_on[datatype],
)

def on_mount(self):
"""
Update datatype out based on the current checkbox
ticks which are determined during `compose().`
"""
datatype_dict = self.get_datatype_dict()
self.set_datatype_out(datatype_dict)

def on_checkbox_changed(self):
"""
When a checkbox is clicked, update the `type_out` attribute
When a checkbox is clicked, update the `datatype_out` attribute
with the datatypes to pass to `make_folders` datatype argument.
"""
type_dict = {
type: self.query_one(f"#tabscreen_{type}_checkbox").value
for type in self.type_config
datatype_dict = self.get_datatype_dict()

# This is slightly wasteful as update entire dict instead
# of changed entry, but is negligible.
self.persistent_settings["tui"]["checkboxes_on"] = datatype_dict

self.project._save_persistent_settings(self.persistent_settings)

self.set_datatype_out(datatype_dict)

def get_datatype_dict(self) -> Dict:
""""""
datatype_dict = {
datatype: self.query_one(f"#tabscreen_{datatype}_checkbox").value
for datatype in self.datatype_config
}
self.type_out = [

return datatype_dict

def set_datatype_out(self, datatype_dict: dict) -> None:
""""""
self.datatype_out = [
datatype
for datatype, is_on in zip(type_dict.keys(), type_dict.values())
for datatype, is_on in zip(
datatype_dict.keys(), datatype_dict.values()
)
if is_on
]


class ClickableInput(Input):
"""
An input widget which emits a `ClickableInput.Clicked`
signal when clicked, containing the input name
`input` and mouse button index `button`.
"""

@dataclass
class Clicked(Message):
input: ClickableInput
button: int

def _on_click(self, click: events.Click) -> None:
self.post_message(self.Clicked(self, click.button))
Loading

0 comments on commit 9542421

Please sign in to comment.