-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Enable saving/loading of math-derived signals in plotlists #5
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,9 @@ def __init__(self, parent): | |
| self.tabs.currentChanged.connect(lambda x: self.tabChanged.emit()) | ||
| layout.addWidget(self.tabs) | ||
|
|
||
| # Make var_lists easily accessible for get_data_item_by_file_id_and_name | ||
| self.var_lists = [] # List to store VarListWidget instances | ||
|
|
||
| self.filter_box = FilterBoxWidget(self.tabs) | ||
| layout.addWidget(self.filter_box) | ||
|
|
||
|
|
@@ -68,8 +71,12 @@ def open_file(self, filepath): | |
| tab_name = os.path.basename(filepath) | ||
| # Create a new tab and add the varListWidget to it. | ||
| self.latest_data_file_name = filepath | ||
| self.tabs.addTab(var_list, tab_name) | ||
| self.sources[filepath] = self.tabs.widget(self.tabs.count() - 1) | ||
| tab_idx = self.tabs.addTab(var_list, tab_name) # addTab returns the index | ||
| # self.sources[filepath] = self.tabs.widget(self.tabs.count() - 1) # Old way | ||
| self.sources[filepath] = var_list # Store the instance directly | ||
| var_list.idx = tab_idx # Assign tab index as an ad-hoc idx if needed for PlotSpec compatibility | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assigning |
||
| self.var_lists.append(var_list) # Add to our list | ||
|
|
||
| self.tabs.setCurrentWidget(var_list) | ||
| self._update_range_slider() | ||
|
|
||
|
|
@@ -81,11 +88,22 @@ def open_file(self, filepath): | |
|
|
||
| def close_file(self, index): | ||
| # Add function for closing the tab here. | ||
| filename = self.tabs.widget(index).filename | ||
| self.tabs.widget(index).close() | ||
| self.tabs.removeTab(index) | ||
| widget_to_close = self.tabs.widget(index) | ||
| filename = widget_to_close.filename | ||
|
|
||
| if widget_to_close in self.var_lists: | ||
| self.var_lists.remove(widget_to_close) | ||
| if filename in self.sources: | ||
| del self.sources[filename] | ||
|
|
||
| widget_to_close.close() # Close the widget first | ||
| self.tabs.removeTab(index) # Then remove tab | ||
|
|
||
| if self.tabs.count() > 0: | ||
| self._update_range_slider() | ||
| else: # Reset slider if no files are open | ||
| self.controller.plot_manager.update_slider_limits(0, 1.e9) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The value |
||
|
|
||
|
|
||
| self.countChanged.emit() | ||
| self.fileClosed[str].emit(filename) | ||
|
|
@@ -99,17 +117,59 @@ def get_latest_data_file_name(self): | |
| def get_data_file(self, idx): | ||
| return self.tabs.widget(idx) | ||
|
|
||
| def get_data_file_by_name(self, name): | ||
| return self.sources[name] | ||
| def get_data_file_by_name(self, name): # name is filepath | ||
| return self.sources.get(name) # Use .get for safer access | ||
|
|
||
| def get_data_item_by_file_id_and_name(self, file_identifier: str, signal_name: str) -> 'DataItem | None': | ||
| """ | ||
| Retrieves a DataItem from one of the loaded files. | ||
| file_identifier can be the file's full path or its stringified tab index at time of PlotSpec creation. | ||
| signal_name is the original_name of the signal. | ||
| """ | ||
| # Try matching by full filepath first (most robust) | ||
| var_list_widget = self.sources.get(file_identifier) | ||
| if var_list_widget: | ||
| model = var_list_widget.model() | ||
| if model: | ||
| data_item = model.get_data_by_name(signal_name) | ||
| if data_item: | ||
| return data_item | ||
|
|
||
| # Fallback: iterate through all var_lists if not found by path (e.g., if file_identifier was an index string) | ||
| # This is less robust if tab order changed or files were closed/reopened. | ||
| for vlw in self.var_lists: | ||
| # Check against filename (if file_identifier was a filename but not full path) | ||
| if os.path.basename(vlw.filename) == file_identifier: | ||
| model = vlw.model() | ||
| if model: | ||
| data_item = model.get_data_by_name(signal_name) | ||
| if data_item: return data_item | ||
|
|
||
| # Check against ad-hoc idx (tab index at time of creation) | ||
| # This relies on VarListWidget having an 'idx' attribute that was set to its tab index. | ||
| if hasattr(vlw, 'idx') and str(vlw.idx) == file_identifier: | ||
| model = vlw.model() | ||
| if model: | ||
| data_item = model.get_data_by_name(signal_name) | ||
| if data_item: return data_item | ||
|
|
||
| print(f"Warning: Could not find DataItem for signal '{signal_name}' from file ID '{file_identifier}'") | ||
| return None | ||
|
|
||
| def get_sources(self): | ||
| return self.sources | ||
|
|
||
| def get_time(self, idx=0): | ||
| if self.tabs.count() == 0: | ||
| return None | ||
| # Ensure idx is valid before trying to access widget | ||
| if idx < 0 or idx >= self.tabs.count(): | ||
| if self.tabs.count() > 0: # Default to the current tab if idx is bad but tabs exist | ||
| return self.tabs.currentWidget().time | ||
| return None # No tabs, no time | ||
| return self.get_data_file(idx).time | ||
|
|
||
|
|
||
| @pyqtSlot(QPoint) | ||
| def on_context_menu_request(self, pos): | ||
| # We only want to bring up the context menu when an actual tab is right-clicked. Check that | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,15 +4,18 @@ | |
|
|
||
| import numpy as np | ||
|
|
||
| from plot_spec import PlotSpec # Added import | ||
|
|
||
|
|
||
| class DataItem(object): | ||
| """ | ||
| Data structure for storing data items in the list widget | ||
| """ | ||
|
|
||
| def __init__(self, var_name, data): | ||
| def __init__(self, var_name, data, plot_spec: PlotSpec | None = None): # Added plot_spec | ||
| self._var_name = var_name | ||
| self._data = data | ||
| self._plot_spec = plot_spec # Added plot_spec | ||
| self._time = None | ||
|
|
||
| @property | ||
|
|
@@ -27,6 +30,14 @@ def data(self): | |
| def time(self): | ||
| return self._time | ||
|
|
||
| @property | ||
| def plot_spec(self): # Added plot_spec property | ||
| return self._plot_spec | ||
|
|
||
| @plot_spec.setter | ||
| def plot_spec(self, value: PlotSpec | None): # Added plot_spec setter | ||
| self._plot_spec = value | ||
|
|
||
| def __repr__(self): | ||
| return self._var_name | ||
|
|
||
|
|
@@ -99,10 +110,29 @@ def data(self, index, role): | |
| def has_key(self, name): | ||
| return name in self._raw_data.index | ||
|
|
||
| def get_data_by_name(self, name): | ||
| data = None | ||
| try: | ||
| data = self._raw_data[name] | ||
| except KeyError: | ||
| print(f"Unknown key: {name}") | ||
| return data | ||
| def get_data_by_name(self, name) -> DataItem | None: | ||
| # Iterates self._data (which is list[DataItem]) and finds the DataItem with the matching var_name. | ||
| for item in self._data: | ||
| if item.var_name == name: | ||
| # Ensure the PlotSpec is created if it's missing for a file-loaded item | ||
| if item.plot_spec is None: | ||
| # Attempt to get a file_source_identifier | ||
| # This DataModel instance itself doesn't store the filename directly in a way | ||
| # that's easily accessible per item here. We'll assume a generic one or improve later if needed. | ||
| # For now, we know it's from this model, which is usually file-based. | ||
| file_id = "unknown_data_model_source" | ||
| # A better approach would be if data_loader.source (filename) was stored in DataModel | ||
| # and accessible here, or if DataItem was initialized with it. | ||
| # Let's assume self.filename could exist if DataModel was enhanced. | ||
| # if hasattr(self, 'filename') and self.filename: | ||
| # file_id = self.filename | ||
|
|
||
| item.plot_spec = PlotSpec( | ||
| name=item.var_name, | ||
| source_type="file", | ||
| original_name=item.var_name, | ||
| file_source_identifier=file_id # Placeholder, actual file ID needs better handling | ||
|
Comment on lines
+123
to
+134
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fallback |
||
| ) | ||
| return item | ||
| print(f"Unknown key: {name} in DataModel") | ||
| return None | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of placeholder strings like
"unknown_source_widget"forfile_idwhen a more specific identifier can't be found is a reasonable fallback. However, if these kinds of identifiers become more common or need to be managed more strictly across different parts of the application (e.g., here and inDataModel), would it be worth defining them as constants in a shared module to ensure consistency and avoid typos?