Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
90a4420
Re #132 Menu and stub for sliders widget
abuts Oct 21, 2025
20228fc
Re #132 Dependency necessary for testing slider widget
abuts Oct 22, 2025
f9f3add
Re #132 Looks like working change on a project level
abuts Oct 24, 2025
5339569
Re #132 working test for MainWindowView changes state of the Project …
abuts Oct 24, 2025
7dac836
Re #132 removed qtbot from test in favour of generic mock
abuts Oct 24, 2025
0578650
Re #164 Revert project widget to its initial state before adding mult…
abuts Oct 27, 2025
e28bb5b
Re #164 stub for sliders_view. Just ugly class and couple of buttons
abuts Oct 27, 2025
4ff0e70
Re #164 Stub for sliders_view and its connection with view widget
abuts Oct 28, 2025
e4d26ec
Re #164 Made satisfactory layout for sliders view
abuts Oct 29, 2025
a143191
Re #164 Fixed tests for view/hide sliders
abuts Oct 29, 2025
d5da3d9
Re #164 added view sliders to exclusions list until project is loaded
abuts Oct 29, 2025
3777b79
Re #164 fixed test_view and modified Settings including Slivers View …
abuts Oct 30, 2025
e9e640e
Merge branch 'main' into 164_sliders_view
abuts Oct 31, 2025
d3597ee
Re #148 sliders which work with properties. Not yet connected to slid…
abuts Oct 31, 2025
4e5504e
Re #148 sliders_view widget populated with sliders
abuts Nov 3, 2025
bdf4c55
Re #148 Acceptable add_sliders method
abuts Nov 3, 2025
3dcb096
Re #148 Sliders work, change project's properties, on calculate graph…
abuts Nov 4, 2025
1d219f1
Re #148 Bug in update_slider_parameters
abuts Nov 4, 2025
625610e
Re #148 Decrease distance between slider widgets
abuts Nov 4, 2025
2f62783
Re #148 Slightly better way of defining sliders labels and initial un…
abuts Nov 5, 2025
87edc5b
Re #148 better treatment for slider widget when edit project is involved
abuts Nov 5, 2025
4b93618
Re #148 fixed test_project and check if edit button indeed hides slid…
abuts Nov 5, 2025
8938f38
Re #148 looks like sliders updating project calculations. Unexpected.…
abuts Nov 6, 2025
28f900e
Re #148 Looks like working project logic. Looks like some bugs in pro…
abuts Nov 7, 2025
736e0b7
Merge branch 'main' into 148_working_sliders
abuts Nov 7, 2025
5253082
Re #148 fixing empty sliders, incomplete
abuts Nov 7, 2025
1633a0d
Re #148 fixed empty slider issue and hung up due to slider out of range
abuts Nov 7, 2025
1391a4b
Re #148 starting write mocks for slider view tests
abuts Nov 7, 2025
82abb27
Re #148 Playing with code of LabeledSlider -- modifying according to …
abuts Nov 7, 2025
2465ae8
Re #148 looks like all delegates connections have been set correctly.…
abuts Nov 10, 2025
2cafa03
Re #148 Properly established connection between table and sliders
abuts Nov 10, 2025
d730b89
Re #148 Tests for LabeledSlider
abuts Nov 10, 2025
df8259e
Re #148 formal changes. Formally separated test for slider class and …
abuts Nov 11, 2025
6d12297
Re #148 Test changes in delegates and TableViewModels related to slid…
abuts Nov 11, 2025
06d2d5c
Re #148 Better access to column delegates
abuts Nov 11, 2025
bf28e3d
Re #148 first most annoying unit test for sliders widget
abuts Nov 12, 2025
d4d795c
Re #148 more unit tests for SlidersViewWidget and couple of important…
abuts Nov 13, 2025
90f1f36
Re #148 Final tests for sliders widget
abuts Nov 13, 2025
7727e56
Re #148 ruff errors
abuts Nov 13, 2025
4d03d05
Re #148 more ruff changes
abuts Nov 13, 2025
a3bab04
Re #148 Formal change -- something wrong with ruff
abuts Nov 13, 2025
21387f7
Re #148 Ruff format applied
abuts Nov 13, 2025
ba72197
Re #148 fixed unit tests for project/test_project.py
abuts Nov 13, 2025
c6325c2
Re #148 Bug due to ruff reformatting
abuts Nov 13, 2025
5e993e8
Re #148 fixed (figure canvas)
abuts Nov 13, 2025
7436406
Re #148 ruff errors
abuts Nov 13, 2025
55b3812
Re #148 Tests for empty sliders
abuts Nov 14, 2025
e95392b
Re #149 unit tests for show sliders widget
abuts Nov 14, 2025
d08054d
Re #149 Ruff errors
abuts Nov 14, 2025
1ff90b7
Re #149 ruff formatting
abuts Nov 14, 2025
7cd7465
Re #149 simplify logic behind storing project view when project is ed…
abuts Nov 14, 2025
e30d594
Re #149 fixed test_project
abuts Nov 14, 2025
e004f23
Re #149 ruff errors
abuts Nov 14, 2025
dbb03d0
Re #149 minor code comments
abuts Nov 14, 2025
3813544
Re #149 Changes from review
abuts Nov 18, 2025
4ad1d5d
Re #149 identified logical bug in reverting properties values
abuts Nov 18, 2025
2dc3ce5
Re #149 added unit tests to catch up issue, identified earlier
abuts Nov 18, 2025
96a9fbd
Re #149 Ruff format and formal changes for git to recognise them
abuts Nov 18, 2025
ffbd6d6
Re #149 fixing ruff format
abuts Nov 19, 2025
799d24d
Re #149 Changes from review and more reliable way of checking changed…
abuts Nov 19, 2025
31578d9
Re #149 Changes from review. Mainly docstring format
abuts Nov 19, 2025
d27a228
Re #149 Ruff errors and changes from review. Mainly docstring format.
abuts Nov 19, 2025
9583d78
Re #149 fixing bugs caused by recent changes
abuts Nov 19, 2025
2ee9fa2
Re #149 ruff errors
abuts Nov 19, 2025
e9a231c
Re #149 fixed unit tests related to changed properties
abuts Nov 19, 2025
eea2eba
Re #149 minor comments
abuts Nov 19, 2025
67cf3f2
Re #162 basic project attached widget functionality
abuts Nov 19, 2025
1d13a15
Re #162 looks like working version of sliders_view.py
abuts Nov 19, 2025
c1526a7
Re #162 reverting to previous connection in ProjectTabWidget->show_sl…
abuts Nov 19, 2025
c195ac0
Re #162 fixed issue with duplicated widget dialogs
abuts Nov 19, 2025
3ec35ce
Re #162 Removed test for model delegates irrelevant any more
abuts Nov 19, 2025
b211310
Re #162 Remove redundant tests for project/test_project.py
abuts Nov 19, 2025
bf522dd
Re #162 removed redundant tests for SlidersViewWidget and fixed remai…
abuts Nov 19, 2025
432533d
Re #162 Ruff format for code
abuts Nov 19, 2025
5b2c1ab
Re #162 ruff format for tests
abuts Nov 19, 2025
d54b37a
Re #162 ruff changes for tests
abuts Nov 19, 2025
755f0ef
Re #162 removed redundant mdi controls
abuts Nov 19, 2025
d6f2a55
Re #162 fixed tests for MDI geometries removing sliders widget from t…
abuts Nov 19, 2025
056f1b6
Re #162 Ruff formatting
abuts Nov 19, 2025
08ebb79
Re #162 fixed test_integration, removed redundant MDI windows
abuts Nov 19, 2025
ac56a08
Re #149 Changes from review.
abuts Nov 24, 2025
b44101c
Re #149 bug from partial deletion
abuts Nov 24, 2025
2f3de6b
Re #149 ruff errors
abuts Nov 24, 2025
8e671d2
Re #149 Erroneously deleted object name after review
abuts Nov 24, 2025
f89decc
Re #149 Changes from review
abuts Nov 26, 2025
d485dc6
Re #149 reverted change in widgets naming algorithm
abuts Nov 26, 2025
99576b6
Re #149 one missing reversion
abuts Nov 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 61 additions & 6 deletions rascal2/ui/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from rascal2.dialogs.settings_dialog import SettingsDialog
from rascal2.dialogs.startup_dialog import PROJECT_FILES, LoadDialog, LoadR1Dialog, NewProjectDialog, StartupDialog
from rascal2.settings import MDIGeometries, Settings, get_global_settings
from rascal2.widgets import ControlsWidget, PlotWidget, TerminalWidget
from rascal2.widgets import ControlsWidget, PlotWidget, SlidersViewWidget, TerminalWidget
from rascal2.widgets.project import ProjectWidget
from rascal2.widgets.startup import StartUpWidget

Expand All @@ -22,6 +22,11 @@ class MainWindowView(QtWidgets.QMainWindow):

def __init__(self):
super().__init__()
# Public interface
self.disabled_elements = []
self.show_sliders = False # no one displays sliders initially except got from configuration
# (not implemented yet)

self.setWindowTitle(MAIN_WINDOW_TITLE)

window_icon = QtGui.QIcon(path_for("logo.png"))
Expand All @@ -38,14 +43,21 @@ def __init__(self):
self.plot_widget = PlotWidget(self)
self.terminal_widget = TerminalWidget()
self.controls_widget = ControlsWidget(self)
self.sliders_view_widget = SlidersViewWidget(self)
self.project_widget = ProjectWidget(self)

self.disabled_elements = []
## protected interface and public properties construction

# define menu controlling switch between table and slider views
self._sliders_menu_control_text = {
"ShowSliders": "&Show Sliders", # if state is show sliders, click will show them
"HideSliders": "&Hide Sliders",
} # if state is show table, click will show sliders

self.create_actions()

self.main_menu = self.menuBar()
self.add_submenus(self.main_menu)
main_menu = self.menuBar()
self.add_submenus(main_menu)

self.create_toolbar()
self.create_status_bar()
Expand Down Expand Up @@ -166,6 +178,20 @@ def create_actions(self):
open_help_action.triggered.connect(self.open_docs)
self.open_help_action = open_help_action

# done this way expecting the value "show_sliders" being stored
# in configuration in a future + "show_sliders" is public for this reason
if self.show_sliders:
# if show_sliders state is True, action will be hide
show_or_hide_slider_action = QtGui.QAction(self._sliders_menu_control_text["HideSliders"], self)
else:
# if display_sliders state is False, action will be show
show_or_hide_slider_action = QtGui.QAction(self._sliders_menu_control_text["ShowSliders"], self)
show_or_hide_slider_action.setStatusTip("Show or Hide Sliders")
show_or_hide_slider_action.triggered.connect(lambda: self.show_or_hide_sliders(None))
self._show_or_hide_slider_action = show_or_hide_slider_action
self._show_or_hide_slider_action.setEnabled(False)
self.disabled_elements.append(self._show_or_hide_slider_action)

open_about_action = QtGui.QAction("&About", self)
open_about_action.setStatusTip("Report RAT version&info")
open_about_action.triggered.connect(self.open_about_info)
Expand Down Expand Up @@ -242,6 +268,8 @@ def add_submenus(self, main_menu: QtWidgets.QMenuBar):

tools_menu = main_menu.addMenu("&Tools")
tools_menu.setObjectName("&Tools")
tools_menu.addAction(self._show_or_hide_slider_action)
tools_menu.addSeparator()
tools_menu.addAction(self.clear_terminal_action)
tools_menu.addSeparator()
tools_menu.addAction(self.setup_matlab_action)
Expand All @@ -251,6 +279,32 @@ def add_submenus(self, main_menu: QtWidgets.QMenuBar):
help_menu.addAction(self.open_about_action)
help_menu.addAction(self.open_help_action)

def show_or_hide_sliders(self, do_show_sliders=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

why does do_show_sliders default to None why not just False?

Copy link
Collaborator Author

@abuts abuts Nov 18, 2025

Choose a reason for hiding this comment

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

because the widget supports triple logic. If you give true or false, it does what requested, but if none, inverts the current state.

"""Depending on current state, show or hide sliders for
Copy link
Contributor

Choose a reason for hiding this comment

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

Docstring is not in the correct format. Please see numpydoc format

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The description there is pretty waigue. Hopefully clarified.

Copy link
Contributor

Choose a reason for hiding this comment

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

Numpydoc is used all over the repo, you can copy from many examples

"""Save the model.
Parameters
----------
save_as : bool
Whether we are saving to the existing save path or to a specified folder.
Returns
-------
: bool
Indicates if the project was saved.
"""

there are also example on the internet https://www.sphinx-doc.org/en/master/usage/extensions/example_numpy.html

Copy link
Collaborator Author

@abuts abuts Nov 19, 2025

Choose a reason for hiding this comment

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

hopefully I have picked it up, but this is not exaclty what is there in different places. BTW, pycharm suggests sligly different format, e.g.

 :returns: dict

table properties within Project class view.

Parameters:
-----------

do_show_sliders: bool,default None
if provided, sets self.show_sliders logical variable into the requested state
(True/False), forcing sliders widget to appear/disappear. if None, applies not to current state.
"""
if do_show_sliders is None:
self.show_sliders = not self.show_sliders
else:
self.show_sliders = do_show_sliders

if self.show_sliders:
self._show_or_hide_slider_action.setText(self._sliders_menu_control_text["HideSliders"])
self.sliders_view_widget.show()
self.project_widget.setWindowTitle("Sliders View")
self.project_widget.stacked_widget.setCurrentIndex(2)
else:
self._show_or_hide_slider_action.setText(self._sliders_menu_control_text["ShowSliders"])
self.sliders_view_widget.hide()
self.project_widget.show_project_view()

def open_about_info(self):
"""Opens about menu containing information about RASCAL gui"""
self.about_dialog.update_rascal_info(self)
Expand Down Expand Up @@ -311,7 +365,9 @@ def setup_mdi(self):
self.setCentralWidget(self.mdi)

def setup_mdi_widgets(self):
"""Performs setup of MDI widgets that relies on the Project existing."""
"""
Performs initialization of MDI widgets that rely on the Project being defined.
"""
self.controls_widget.setup_controls()
self.project_widget.show_project_view()
self.plot_widget.clear()
Expand All @@ -333,7 +389,6 @@ def reset_mdi_layout(self):
window.showMinimized()
else:
window.showNormal()

window.setGeometry(x, y, width, height)

def save_mdi_layout(self):
Expand Down
2 changes: 2 additions & 0 deletions rascal2/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rascal2.widgets.controls import ControlsWidget
from rascal2.widgets.inputs import AdaptiveDoubleSpinBox, MultiSelectComboBox, MultiSelectList, get_validated_input
from rascal2.widgets.plot import PlotWidget
from rascal2.widgets.sliders_view import SlidersViewWidget
from rascal2.widgets.terminal import TerminalWidget

__all__ = [
Expand All @@ -11,4 +12,5 @@
"MultiSelectList",
"PlotWidget",
"TerminalWidget",
"SlidersViewWidget",
]
12 changes: 10 additions & 2 deletions rascal2/widgets/project/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,27 @@ def __init__(self, parent):
self.stacked_widget = QtWidgets.QStackedWidget()
self.stacked_widget.addWidget(project_view)
self.stacked_widget.addWidget(project_edit)
self.stacked_widget.addWidget(self.parent.sliders_view_widget)

layout = QtWidgets.QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.stacked_widget)
self.setLayout(layout)

def create_project_view(self) -> None:
def create_project_view(self) -> QtWidgets.QWidget:
"""Creates the project (non-edit) view"""
project_widget = QtWidgets.QWidget()
main_layout = QtWidgets.QVBoxLayout()
main_layout.setSpacing(20)

show_sliders_button = QtWidgets.QPushButton("Show sliders", self, objectName="ShowSliders")
show_sliders_button.clicked.connect(lambda: self.parent.show_or_hide_sliders(True))

self.edit_project_button = QtWidgets.QPushButton("Edit Project", self, icon=QtGui.QIcon(path_for("edit.png")))
self.edit_project_button.clicked.connect(self.show_edit_view)
button_layout = QtWidgets.QHBoxLayout()
button_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
button_layout.addWidget(show_sliders_button)
button_layout.addWidget(self.edit_project_button)

main_layout.addLayout(button_layout)
Expand Down Expand Up @@ -142,7 +147,7 @@ def create_project_view(self) -> None:

return project_widget

def create_edit_view(self) -> None:
def create_edit_view(self) -> QtWidgets.QWidget:
"""Creates the project edit view"""

edit_project_widget = QtWidgets.QWidget()
Expand Down Expand Up @@ -360,6 +365,8 @@ def show_project_view(self) -> None:

def show_edit_view(self) -> None:
"""Show edit view"""

# will be updated according to edit changes
self.update_project_view(0)
self.setWindowTitle("Edit Project")
self.parent.controls_widget.run_button.setEnabled(False)
Expand Down Expand Up @@ -540,6 +547,7 @@ def __init__(self, fields: list[str], parent, edit_mode: bool = False):
self.tables[field] = DataWidget(field, self)
else:
self.tables[field] = ProjectFieldWidget(field, self)
self.tables[field].setObjectName(field)
layout.addWidget(self.tables[field])

scroll_area = QtWidgets.QScrollArea()
Expand Down
48 changes: 39 additions & 9 deletions rascal2/widgets/project/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,27 @@ def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
elif role == QtCore.Qt.ItemDataRole.CheckStateRole and self.index_header(index) == "fit":
return QtCore.Qt.CheckState.Checked if data else QtCore.Qt.CheckState.Unchecked

def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole) -> bool:
def setData(
self, index: QtCore.QModelIndex, value, role=QtCore.Qt.ItemDataRole.EditRole, recalculate_proj=True
) -> bool:
"""Implement abstract setData method of QAbstractTableModel.

Parameters
----------
index: QtCore.QModelIndex
QModelIndex representing the row and column indices of edited cell wrt. the edited table
value:
new value of appropriate cell of the table.
role: QtCore.Qt.ItemDataRole
not sure what it is but apparently controls table behaviour amd needs to be Edit.
it nof Edit, method does nothing.
recalculate_proj: bool,default True
Additional control for RAT project recalculation. Set it to False when modifying
a bunch of properties in a loop changing it to True for the last value to recalculate
project and update all table's dependent widgets.
IMPORTANT: ensure last value differs from the existing one for this property as project
will be not recalculated otherwise.
"""
if role == QtCore.Qt.ItemDataRole.EditRole or role == QtCore.Qt.ItemDataRole.CheckStateRole:
row = index.row()
param = self.index_header(index)
Expand All @@ -93,7 +113,7 @@ def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole) -> bool:
return False
if not self.edit_mode:
# recalculate plots if value was changed
recalculate = self.index_header(index) == "value"
recalculate = self.index_header(index) == "value" and recalculate_proj
self.parent.update_project(recalculate)
self.dataChanged.emit(index, index)
return True
Expand Down Expand Up @@ -175,6 +195,7 @@ def __init__(self, field: str, parent):
self.parent = parent
self.project_widget = parent.parent
self.table = QtWidgets.QTableView(parent)

self.table.horizontalHeader().setCascadingSectionResizes(True)
self.table.setMinimumHeight(100)

Expand Down Expand Up @@ -231,7 +252,7 @@ def resize_columns(self):

header.setStretchLastSection(True)

def update_model(self, classlist):
def update_model(self, classlist: ratapi.classlist.ClassList):
"""Update the table model to synchronise with the project field."""
self.model = self.classlist_model(classlist, self)

Expand All @@ -248,10 +269,18 @@ def update_model(self, classlist):
def set_item_delegates(self):
"""Set item delegates and open persistent editors for the table."""
for i, header in enumerate(self.model.headers):
self.table.setItemDelegateForColumn(
i + self.model.col_offset,
delegates.ValidatedInputDelegate(self.model.item_type.model_fields[header], self.table),
)
delegate = delegates.ValidatedInputDelegate(self.model.item_type.model_fields[header], self.table)
self.table.setItemDelegateForColumn(i + self.model.col_offset, delegate)

def get_item_delegates(self, fields_list: list):
"""Return list of delegates attached to the fields
with the names provided as input
"""
dlgts = []
for i, header in enumerate(self.model.headers):
if header in fields_list:
dlgts.append(self.table.itemDelegateForColumn(i + self.model.col_offset))
return dlgts

def append_item(self):
"""Append an item to the model if the model exists."""
Expand Down Expand Up @@ -354,13 +383,14 @@ class ParameterFieldWidget(ProjectFieldWidget):
def set_item_delegates(self):
for i, header in enumerate(self.model.headers):
if header in ["min", "value", "max"]:
self.table.setItemDelegateForColumn(i + 1, delegates.ValueSpinBoxDelegate(header, self.table))
delegate = delegates.ValueSpinBoxDelegate(header, self.table)
self.table.setItemDelegateForColumn(i + 1, delegate)
else:
self.table.setItemDelegateForColumn(
i + 1, delegates.ValidatedInputDelegate(self.model.item_type.model_fields[header], self.table)
)

def update_model(self, classlist):
def update_model(self, classlist: ratapi.classlist.ClassList):
super().update_model(classlist)
header = self.table.horizontalHeader()
header.setSectionResizeMode(
Expand Down
Loading
Loading