@@ -230,6 +230,9 @@ def void_formatter(array, *args, **kwargs):
230230 arrays = [k for k , v in self .data .items () if self ._display_in_grid (k , v )]
231231 self .add_list_items (arrays )
232232
233+ # tracking data changes
234+ self .arraywidget .model_data .dataChanged .connect (self .data_changed )
235+
233236 return True
234237
235238 def _reset (self ):
@@ -277,6 +280,22 @@ def setup_menu_bar(self):
277280 help_menu .addAction (create_action (self , _ ('Online Objects and Functions (API) &Reference' ),
278281 triggered = self .open_api_documentation ))
279282
283+ def data_changed (self ):
284+ # We do not set self._unsaved_modifications to True because if users click on `Discard` button
285+ # (which calls reject_changes) or choose to display another array, all temporary changes are lost.
286+ # `update_title` relies on _is_unsaved_modifications() which checks both self._unsaved_modifications
287+ # and self.arraywidget.dirty
288+ self .update_title ()
289+
290+ @property
291+ def unsaved_modifications (self ):
292+ return self ._unsaved_modifications
293+
294+ @unsaved_modifications .setter
295+ def unsaved_modifications (self , unsaved_modifications ):
296+ self ._unsaved_modifications = unsaved_modifications
297+ self .update_title ()
298+
280299 def add_list_item (self , name ):
281300 listitem = QListWidgetItem (self ._listwidget )
282301 listitem .setText (name )
@@ -339,7 +358,7 @@ def update_mapping(self, value):
339358
340359 # 3) mark session as dirty if needed
341360 if len (changed_displayable_keys ) > 0 or deleted_displayable_keys :
342- self ._unsaved_modifications = True
361+ self .unsaved_modifications = True
343362
344363 # 4) change displayed array in the array widget
345364 # only display first result if there are more than one
@@ -465,6 +484,10 @@ def update_title(self):
465484 title = [name ]
466485 # extra info
467486 title += [self .title ]
487+ # add '*' at the end of the title if unsaved modifications.
488+ if self ._is_unsaved_modifications ():
489+ title += ['*' ]
490+ # set title
468491 self .setWindowTitle (' - ' .join (title ))
469492
470493 def set_current_array (self , array , name ):
@@ -512,7 +535,7 @@ def new(self):
512535 self ._reset ()
513536 self .arraywidget .set_data (np .empty (0 ))
514537 self .set_current_file (None )
515- self ._unsaved_modifications = False
538+ self .unsaved_modifications = False
516539 self .statusBar ().showMessage ("Viewer has been reset" , 4000 )
517540
518541 def _open_file (self , filepath ):
@@ -532,7 +555,7 @@ def _open_file(self, filepath):
532555 self .statusBar ().showMessage ("File {} loaded" .format (os .path .basename (filepath )), 4000 )
533556 self ._add_arrays (session )
534557 self ._listwidget .setCurrentRow (0 )
535- self ._unsaved_modifications = False
558+ self .unsaved_modifications = False
536559
537560 def open (self ):
538561 if self ._ask_to_save_if_unsaved_modifications ():
@@ -563,7 +586,7 @@ def _save_data(self, filepath):
563586 session = Session ({k : v for k , v in self .data .items () if self ._display_in_grid (k , v )})
564587 session .save (filepath )
565588 self .set_current_file (filepath )
566- self ._unsaved_modifications = False
589+ self .unsaved_modifications = False
567590 self .statusBar ().showMessage ("Arrays saved in file {}" .format (filepath ), 4000 )
568591
569592 def save (self ):
@@ -642,13 +665,14 @@ def closeEvent(self, event):
642665 event .ignore ()
643666
644667 def apply_changes (self ):
645- # update _unsaved_modifications only if 1 or more changes have been applied
668+ # update unsaved_modifications (and thus title) only if at least 1 change has been applied
646669 if self .arraywidget .dirty :
647- self ._unsaved_modifications = True
670+ self .unsaved_modifications = True
648671 self .arraywidget .accept_changes ()
649672
650673 def discard_changes (self ):
651674 self .arraywidget .reject_changes ()
675+ self .update_title ()
652676
653677 def get_value (self ):
654678 """Return modified array -- this is *not* a copy"""
0 commit comments