Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 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
94c9740
Re XXX Merge branch 'main' into sliders_with_delegates
abuts Nov 28, 2025
ff95014
Re XXX fixing merge errors
abuts Nov 28, 2025
13d6b4c
Re XXX fixed test failing due to toggle_sliders rename
abuts Nov 28, 2025
ab3b5fe
Re XXX fix tests failing due to merge with master
abuts Nov 30, 2025
7db4742
Re XXX modify sliders to use standard events blocking method instead …
abuts Nov 30, 2025
fd33b9b
Re XXX Ruff formatting
abuts Nov 30, 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
9 changes: 5 additions & 4 deletions rascal2/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,11 @@ def _missing_(cls, value):
class MDIGeometries(BaseModel):
"""Model for storing window positions and sizes."""

plots: WindowGeometry = Field(max_length=5, min_length=5)
project: WindowGeometry = Field(max_length=5, min_length=5)
terminal: WindowGeometry = Field(max_length=5, min_length=5)
controls: WindowGeometry = Field(max_length=5, min_length=5)
Plots: WindowGeometry = Field(max_length=5, min_length=5)
Project: WindowGeometry = Field(max_length=5, min_length=5)
Terminal: WindowGeometry = Field(max_length=5, min_length=5)
FittingControls: WindowGeometry = Field(max_length=5, min_length=5)
SlidersView: WindowGeometry = Field(max_length=5, min_length=5)


class Settings(BaseModel, validate_assignment=True, arbitrary_types_allowed=True):
Expand Down
142 changes: 110 additions & 32 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,10 @@ 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 @@ -39,11 +43,14 @@ def __init__(self):
self.terminal_widget = TerminalWidget()
self.controls_widget = ControlsWidget(self)
self.project_widget = ProjectWidget(self)
self.sliders_view_widget = SlidersViewWidget(self)

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

self.create_actions()
self.create_menus()

self.add_submenus()

self.create_toolbar()
self.create_status_bar()

Expand All @@ -56,6 +63,15 @@ def __init__(self):
self.setCentralWidget(self.startup_dlg)

self.about_dialog = AboutDialog(self)
# dictionary of main widgets present in the main operation area controlled
# by mdi interface.
# There are other widgets (sliders_view) which are initially hidden.
self._main_window_widgets = {
"Plots": self.plot_widget,
"Project": self.project_widget,
"Terminal": self.terminal_widget,
"Fitting Controls": self.controls_widget,
}

def closeEvent(self, event):
if self.presenter.ask_to_save_project():
Expand Down Expand Up @@ -162,13 +178,17 @@ def create_actions(self):
self.open_help_action.setIcon(QtGui.QIcon(path_for("help.png")))
self.open_help_action.triggered.connect(self.open_docs)

self.toggle_slider_action = QtGui.QAction("Show &Sliders", self)
self.toggle_slider_action.setProperty("show_text", "Show &Sliders")
self.toggle_slider_action.setProperty("hide_text", "Hide &Sliders")
self.toggle_slider_action.setStatusTip("Show or Hide Sliders")
self.toggle_slider_action.triggered.connect(self.toggle_sliders)
self.toggle_slider_action.setEnabled(False)
self.disabled_elements.append(self.toggle_slider_action)
self._toggle_slider_action = QtGui.QAction("Show &Sliders", self)
self._toggle_slider_action.setProperty("show_text", "Show &Sliders")
self._toggle_slider_action.setProperty("hide_text", "Hide &Sliders")
self._toggle_slider_action.setStatusTip("Show or Hide Sliders")
self._toggle_slider_action.triggered.connect(lambda: self.toggle_sliders(None))
# done this way expecting the value "show_sliders" being stored
# in configuration in a future + "show_sliders" is public for this reason
self.toggle_sliders(self.show_sliders)
if not self.show_sliders:
self._toggle_slider_action.setEnabled(False)
self.disabled_elements.append(self._toggle_slider_action)

self.open_about_action = QtGui.QAction("&About", self)
self.open_about_action.setStatusTip("Report RAT version&info")
Expand Down Expand Up @@ -208,7 +228,7 @@ def create_actions(self):
self.setup_matlab_action.setStatusTip("Set the path of the MATLAB executable")
self.setup_matlab_action.triggered.connect(lambda: self.show_settings_dialog(tab_name="Matlab"))

def create_menus(self):
def add_submenus(self):
"""Add sub menus to the main menu bar"""
main_menu = self.menuBar()
main_menu.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.PreventContextMenu)
Expand Down Expand Up @@ -241,7 +261,8 @@ def create_menus(self):
self.disabled_elements.append(windows_menu)

tools_menu = main_menu.addMenu("&Tools")
tools_menu.addAction(self.toggle_slider_action)
tools_menu.setObjectName("&Tools")
tools_menu.addAction(self._toggle_slider_action)
tools_menu.addSeparator()
tools_menu.addAction(self.clear_terminal_action)
tools_menu.addSeparator()
Expand All @@ -251,16 +272,54 @@ def create_menus(self):
help_menu.addAction(self.open_about_action)
help_menu.addAction(self.open_help_action)

def toggle_sliders(self):
"""Toggles sliders for the fitted parameters in project class view."""
show_text = self.toggle_slider_action.property("show_text")
if self.toggle_slider_action.text() == show_text:
hide_text = self.toggle_slider_action.property("hide_text")
self.toggle_slider_action.setText(hide_text)
self.project_widget.show_slider_view()
def toggle_sliders(self, do_show_sliders=None):
"""Depending on current state, show or hide sliders for
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.toggle_slider_action.setText(show_text)
self.project_widget.show_project_view()
self.show_sliders = do_show_sliders

# ignore show/hide operations if project and its mdi container are not defined
if self.sliders_view_widget.mdi_holder is None:
return

if self.show_sliders:
sliders_text = self._toggle_slider_action.property("hide_text")
self.sliders_view_widget.show()
else:
sliders_text = self._toggle_slider_action.property("show_text")
self.sliders_view_widget.hide()
self._toggle_slider_action.setText(sliders_text)

def sliders_view_enabled(self, is_enabled: bool, prev_call_vis_sliders_state: bool = False):
"""Makes sliders view button in menu enabled or disabled depending
on the state of the input parameters.

Used by: project widget to control menu when project editing is enabled.

Parameters:
-----------
is_enabled: bool,
if True, slider state should be enabled, if False - disabled.
prev_call_vis_sliders_state : bool,default False
logical stating what sliders view widget view state was when this method was called
when slider state was disabled
"""
self._toggle_slider_action.setEnabled(is_enabled)

# hide sliders when disabled or else
if is_enabled:
self.toggle_sliders(do_show_sliders=prev_call_vis_sliders_state)
else:
self.toggle_sliders(do_show_sliders=False)

def open_about_info(self):
"""Opens about menu containing information about RASCAL gui"""
Expand Down Expand Up @@ -299,24 +358,26 @@ def setup_mdi(self):
"""Creates the multi-document interface"""
# if windows are already created, don't set them up again,
# just refresh the widget data
if len(self.mdi.subWindowList()) == 4:
if len(self.mdi.subWindowList()) == 5:
self.setup_mdi_widgets()
return

widgets = {
"Plots": self.plot_widget,
"Project": self.project_widget,
"Terminal": self.terminal_widget,
"Fitting Controls": self.controls_widget,
}
self.setup_mdi_widgets()

for title, widget in reversed(widgets.items()):
for title, widget in reversed(self._main_window_widgets.items()):
widget.setWindowTitle(title)
window = self.mdi.addSubWindow(
widget, QtCore.Qt.WindowType.WindowMinMaxButtonsHint | QtCore.Qt.WindowType.WindowTitleHint
)
window.setWindowTitle(title)
# Add sliders view widget separately, as it will behave and is controlled differently from other
# mdi windows
self.mdi.addSubWindow(
self.sliders_view_widget,
QtCore.Qt.WindowType.WindowMinMaxButtonsHint | QtCore.Qt.WindowType.WindowTitleHint,
)
self.sliders_view_widget.setWindowTitle("Sliders View")

self.reset_mdi_layout()
self.startup_dlg = self.takeCentralWidget()
self.setCentralWidget(self.mdi)
Expand All @@ -330,17 +391,34 @@ def setup_mdi_widgets(self):
self.plot_widget.clear()
self.terminal_widget.clear()
self.terminal_widget.write_startup()
self.sliders_view_widget.init()

def reset_mdi_layout(self):
"""Reset MDI layout to the default."""

main_widget_names = self._main_window_widgets.keys()
if self.settings.mdi_defaults is None:
# logic expects "Sliders View" the only widget in the mdi list
slider_view_wrapper = None
for window in self.mdi.subWindowList():
window.showNormal()
if window.windowTitle() in main_widget_names:
window.showNormal()
else:
window.hide()
slider_view_wrapper = window
self.mdi.tileSubWindows()
self.sliders_view_widget.mdi_holder = slider_view_wrapper
else:
# reliability check. Can we have project saved previously with mdi defaults
# and initialized here newer entering the "if mdi_defaults" loop above?
if self.sliders_view_widget.mdi_holder is None:
for window in self.mdi.subWindowList():
if window.windowTitle() == "Sliders View":
self.sliders_view_widget.mdi_holder = window

for window in self.mdi.subWindowList():
# get corresponding MDIGeometries entry for the widget
widget_name = window.windowTitle().lower().split(" ")[-1]
widget_name = window.windowTitle().replace(" ", "")
x, y, width, height, minimized = getattr(self.settings.mdi_defaults, widget_name)
if minimized:
window.showMinimized()
Expand All @@ -353,7 +431,7 @@ def save_mdi_layout(self):
geoms = {}
for window in self.mdi.subWindowList():
# get corresponding MDIGeometries entry for the widget
widget_name = window.windowTitle().lower().split(" ")[-1]
widget_name = window.windowTitle().replace(" ", "")
geom = window.geometry()
geoms[widget_name] = (geom.x(), geom.y(), geom.width(), geom.height(), window.isMinimized())

Expand Down
4 changes: 2 additions & 2 deletions rascal2/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +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.project.slider_view import SliderViewWidget
from rascal2.widgets.sliders_view import SlidersViewWidget
from rascal2.widgets.terminal import TerminalWidget

__all__ = [
Expand All @@ -12,5 +12,5 @@
"MultiSelectList",
"PlotWidget",
"TerminalWidget",
"SliderViewWidget",
"SlidersViewWidget",
]
10 changes: 10 additions & 0 deletions rascal2/widgets/delegates.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
class ValidatedInputDelegate(QtWidgets.QStyledItemDelegate):
"""Item delegate for validated inputs."""

# create custom signal to send to labelled sliders when contents of a cell in
# a table class have been changed
edit_finished_inform_sliders = QtCore.pyqtSignal(QtCore.QModelIndex, object)

def __init__(self, field_info, parent, remove_items: list[int] = None, open_on_show: bool = False):
super().__init__(parent)
self.table = parent
Expand Down Expand Up @@ -50,6 +54,7 @@ def setEditorData(self, _editor: QtWidgets.QWidget, index):
def setModelData(self, _editor, model, index):
data = self.widget.get_data()
model.setData(index, data, QtCore.Qt.ItemDataRole.EditRole)
self.edit_finished_inform_sliders.emit(index, self.field_info)


class CustomFileFunctionDelegate(QtWidgets.QStyledItemDelegate):
Expand Down Expand Up @@ -97,6 +102,10 @@ class ValueSpinBoxDelegate(QtWidgets.QStyledItemDelegate):

"""

# create custom signal to send to labelled sliders when contents of a cell in
# a table cell attached to sliders have been changed
edit_finished_inform_sliders = QtCore.pyqtSignal(QtCore.QModelIndex, object)

def __init__(self, field: Literal["min", "value", "max"], parent):
super().__init__(parent)
self.table = parent
Expand Down Expand Up @@ -130,6 +139,7 @@ def setEditorData(self, editor: AdaptiveDoubleSpinBox, index):
def setModelData(self, editor, model, index):
data = editor.value()
model.setData(index, data, QtCore.Qt.ItemDataRole.EditRole)
self.edit_finished_inform_sliders.emit(index, self.field)


class ProjectFieldDelegate(QtWidgets.QStyledItemDelegate):
Expand Down
Loading
Loading