epicure.preferences

EpiCure preferences functions

Proposes to set and save the shortcut preferences so that each user can choose his/her favorite keys. This is loaded from and saved to a file called epicure_preferences.pkl placed in the home directory of the user, in the .napari folder.

  1"""
  2    **EpiCure preferences functions**
  3
  4    Proposes to set and save the shortcut preferences so that each user can choose his/her favorite keys.
  5    This is loaded from and saved to a file called `epicure_preferences.pkl` placed in the home directory of the user, in the .napari folder. 
  6"""
  7
  8from qtpy.QtWidgets import QPushButton, QVBoxLayout, QTabWidget, QWidget, QComboBox, QLabel, QLineEdit, QGroupBox, QHBoxLayout, QColorDialog
  9#from qtpy.QtGui import QColor
 10import napari
 11import epicure.Utils as ut
 12from pathlib import Path
 13import os, pickle
 14from sys import platform
 15
 16def edit_preferences():
 17    """ Launch preferences edition interface"""
 18    viewer = napari.current_viewer()
 19    prefgui = PreferencesGUI( viewer )
 20    return prefgui
 21
 22class PreferencesGUI( QWidget ):
 23    """ Handles user preferences for shortcuts, default widget state """
 24
 25    def __init__(self, napari_viewer):
 26        """ Initialize the tab with the different widgets """
 27        super().__init__()
 28        
 29        ## preferences (shortcuts, plugin state) object
 30        self.pref = Preferences()
 31        
 32        layout = QVBoxLayout()
 33        tabs = QTabWidget()
 34        tabs.setObjectName("Preferences")
 35        
 36        ## shortcut and plugin state preferences tabs
 37        self.shortcuts = ShortCut( napari_viewer, self.pref )
 38        tabs.addTab( self.shortcuts, "Shortcuts Config." )
 39        self.displays = DisplaySettings( napari_viewer, self.pref )
 40        tabs.addTab( self.displays, "Display Config." )
 41        layout.addWidget(tabs)
 42
 43        ## save option
 44        self.save_pref = QPushButton("Save preferences", parent=self)
 45        layout.addWidget( self.save_pref )
 46        self.save_pref.clicked.connect( self.save )
 47
 48        ## add to main interface
 49        self.setLayout( layout )
 50        #napari_viewer.window.add_dock_widget( main_widget, name="Preferences" )
 51
 52    def save( self ):
 53        """ Save current preferences: update them and save to default file """
 54        self.shortcuts.update_pref()
 55        self.pref.save()
 56        
 57
 58class Preferences():
 59    """ Handles user-specific preferences (shortcuts, widgets states) """
 60    
 61    def __init__( self ):
 62        """ Initialise file path, load current preferences"""
 63        self.build_preferences_path()
 64        #print("Running on "+platform.lower())
 65        
 66        self.ctl = "Control"
 67        self.alt = "Alt"
 68        if platform.lower() == "darwin":
 69            self.ctl = "Command"
 70
 71        self.load_default_shortcuts()
 72        self.load_default_settings()
 73        if os.path.exists( self.preference_path ):
 74            self.load()
 75
 76    def build_preferences_path( self ):
 77        """ Build (create directories if necessary) preference path """
 78        home_dir = Path.home()
 79        self.preference_path = os.path.join( home_dir, ".napari" )
 80        if not os.path.exists( self.preference_path ):
 81            os.mkdir( self.preference_path )
 82        self.preference_path = os.path.join( self.preference_path, "epicure_preferences.pkl" )
 83
 84    def save( self ):
 85        """ Save the current preferences to the preference files in user home """
 86        outfile = open( self.preference_path, "wb" )
 87        pickle.dump( self.shortcuts, outfile )
 88        pickle.dump( self.settings, outfile )
 89        outfile.close()
 90        print( "Preferences saved in file "+self.preference_path )
 91
 92    def set_preferences( self, default_prefs, prefs ):
 93        """ Merge (recursively) the preferences with the default ones """
 94        for key, vals in prefs.items():
 95            if key in default_prefs.keys():
 96                if isinstance( vals, dict ):
 97                    self.set_preferences( default_prefs[key], vals )
 98                else:
 99                    default_prefs[key] = vals
100            else:
101                default_prefs[key] = vals
102
103    def load( self ):
104       """ Load the current preferences to the preference files in user home """
105       infile = open( self.preference_path, "rb" )
106       shortcuts = pickle.load( infile )
107       self.set_preferences( self.shortcuts, shortcuts )
108       try:
109           settings = pickle.load( infile )
110           #print(settings)
111           self.set_preferences( self.settings, settings )
112           #print(self.settings)
113       except:
114           self.load_default_settings()
115       #print(self.shortcuts)
116       infile.close()
117       #print( "Preferences loaded from file "+self.preference_path )
118
119    def get_settings( self ):
120        """ Return the dict of prefered settings (widget state) """
121        return self.settings
122
123    def get_shortcuts( self ):
124        """ Return the dict of shortcuts """
125        return self.shortcuts
126
127    def add_key_shortcut( self, main_type, shortname, fulltext, key ):
128        """ Add a keyboard shortcut """
129        if main_type not in self.shortcuts.keys():
130            self.shortcuts[ main_type ] = {}
131        ## initialize the new shortcut object
132        self.shortcuts[ main_type ][ shortname ] = {}
133        sc = self.shortcuts[ main_type ][ shortname ]
134        sc["type"] = "key"
135        sc["text"] = fulltext
136        sc["key"] = key
137    
138    def add_click_shortcut( self, main_type, shortname, fulltext, button, modifiers=None ):
139        """ Add a keyboard shortcut """
140        if main_type not in self.shortcuts.keys():
141            self.shortcuts[ main_type ] = {}
142        ## initialize the new shortcut object
143        self.shortcuts[ main_type ][ shortname ] = {}
144        sc = self.shortcuts[ main_type ][ shortname ]
145        sc["type"] = "click"
146        sc["text"] = fulltext
147        sc["button"] = button
148        if modifiers is not None:
149            sc["modifiers"] = ["Control" if item=="Command" else item for item in modifiers] 
150
151
152    def load_default_shortcuts( self ):
153        """ Load all default shortcuts """
154
155        self.shortcuts = {}
156
157        ## General shortcuts
158        self.add_key_shortcut( "General", shortname="show help", fulltext="show/hide overlay help message", key="h" )
159        self.add_key_shortcut( "General", shortname="show all", fulltext="show all shortcuts in a separate window", key="a" )
160        self.add_key_shortcut( "General", shortname="save segmentation", fulltext="save the segmentation and epicure files", key="s" )
161        self.add_key_shortcut( "General", shortname="save movie", fulltext="save the movie with current display", key="Shift-s" )
162
163        ## Labels edition (static) shortcuts
164        self.add_key_shortcut( "Labels", shortname="unused paint", fulltext="set the current label to unused value and go to paint mode", key="n" )
165        self.add_key_shortcut( "Labels", shortname="unused fill", fulltext="set the current label to unused value and go to fill mode", key="Shift-n" )
166        self.add_key_shortcut( "Labels", shortname="swap mode", fulltext="then <"+self.ctl+">+Left click on one cell to another to swap their values", key="w" )
167
168        self.add_click_shortcut( "Labels", shortname="erase", fulltext="erase the cell under the click", button="Right", modifiers=None )
169        self.add_click_shortcut( "Labels", shortname="merge", fulltext="drag-click from one cell to another to merge them", button="Left", modifiers=["Control"] )
170        self.add_click_shortcut( "Labels", shortname="split accross", fulltext="drag-click in the cell to split into 2 cells ", button="Right", modifiers=["Control"] )
171        self.add_click_shortcut( "Labels", shortname="split draw", fulltext="drag-click draw a junction to split in 2 cells", button="Right", modifiers=["Alt"] )
172        self.add_click_shortcut( "Labels", shortname="redraw junction", fulltext="drag-click draw a junction to correct it", button="Left", modifiers=["Alt"] )
173        self.add_key_shortcut( "Labels", shortname="draw junction mode", fulltext="Draw junction(s) mode ON", key="j" )
174        self.add_click_shortcut( "Labels", shortname="drawing junction", fulltext="Draw junction mode ON. Drag-click draw a junction to create new cell(s)", button="Left", modifiers=["Control"] )
175        
176        ## Seeds (manual segmentation) shortcuts
177        self.add_key_shortcut( "Seeds", shortname="new seed", fulltext="<key shortcut> then left-click to place a seed", key="e" )
178        
179        ## Groups shortcuts
180        self.add_click_shortcut( "Groups", shortname="add group", fulltext="add the clicked cell to the current group", button="Left", modifiers=["Shift"] )
181        self.add_click_shortcut( "Groups", shortname="remove group", fulltext="remove the clicked cell from the group", button="Right", modifiers=["Shift"] )
182        
183        ## events edition shortcuts
184        self.add_key_shortcut( "Events", shortname="next", fulltext="zoom on next event", key="Space" )
185        self.add_click_shortcut( "Events", shortname="zoom", fulltext="Zoom on the clicked event", button="Left", modifiers=["Control", "Alt"] )
186        self.add_click_shortcut( "Events", shortname="delete", fulltext="Remove the clicked event", button="Right", modifiers=["Control", "Alt"] )
187        self.add_click_shortcut( "Events", shortname="add division", fulltext="add a division: drag-click from first to second daugther", button="Left", modifiers=["Control", "Shift"] )
188        self.add_click_shortcut( "Events", shortname="add extrusion", fulltext="add an extrusion: click on the last cell of the track", button="Right", modifiers=["Control", "Shift"] )
189
190        ## Tracks edition shortcuts
191        self.add_key_shortcut( "Tracks", shortname="show", fulltext="show/hide the tracks", key="r" )
192        self.add_key_shortcut( "Tracks", shortname="lineage color", fulltext="color the tracks by lineage", key="l" )
193        self.add_key_shortcut( "Tracks", shortname="mode", fulltext="on/off track editing mode", key="t" )
194        self.add_click_shortcut( "Tracks", shortname="merge first", fulltext="+track mode ON. Merge tracks: select the first", button="Left" )
195        self.add_click_shortcut( "Tracks", shortname="merge second", fulltext="+trackmode ON. Merge tracks: selec the second", button="Right" )
196        self.add_click_shortcut( "Tracks", shortname="split track", fulltext="+trackmode ON. Split the track temporally in 2", button="Right", modifiers=["Shift"] )
197        self.add_click_shortcut( "Tracks", shortname="start manual", fulltext="+trackmode ON. Start manual tracking, clicking on cells", button="Left", modifiers=["Control"] )
198        self.add_click_shortcut( "Tracks", shortname="end manual", fulltext="+trackmode ON. Finish manual tracking", button="Right", modifiers=["Control"] )
199        self.add_click_shortcut( "Tracks", shortname="interpolate first", fulltext="+trackmode ON. Interpolate temporally labels: select first", button="Left", modifiers=["Alt"] )
200        self.add_click_shortcut( "Tracks", shortname="interpolate second", fulltext="+trackmode ON. Interpolate temporally labels: select second", button="Right", modifiers=["Alt"] )
201        self.add_click_shortcut( "Tracks", shortname="swap", fulltext="+trackmode ON. Drag click to swap 2 tracks from current frame", button="Left", modifiers=["Shift"] )
202        self.add_click_shortcut( "Tracks", shortname="delete", fulltext="+trackmode ON. Delete all the track from current frame", button="Right", modifiers=["Control", "Alt"]  )
203
204        ## Visualisation option shortcuts
205        self.add_key_shortcut( "Display", shortname="vis. segmentation", fulltext="show/hide segmentation layer", key="b" )
206        self.add_key_shortcut( "Display", shortname="vis. movie", fulltext="show/hide movie layer", key="v" )
207        self.add_key_shortcut( "Display", shortname="vis. event", fulltext="show.hide events layer", key="x" )
208        self.add_key_shortcut( "Display", shortname="only movie", fulltext="show ONLY movie layer on/off", key="c" )
209        self.add_key_shortcut( "Display", shortname="light view", fulltext="on/off light segmentation view", key="d" )
210        self.add_key_shortcut( "Display", shortname="skeleton", fulltext="show/hide/update segmentation skeleton", key="k" )
211        self.add_key_shortcut( "Display", shortname="show side", fulltext="view layers side by side on/off", key="z" )
212        self.add_key_shortcut( "Display", shortname="grid", fulltext="show/hide grid", key="g" )
213        self.add_key_shortcut( "Display", shortname="increase", fulltext="increase label contour size", key="Control-c" )
214        self.add_key_shortcut( "Display", shortname="decrease", fulltext="decrease label contour size", key="Control-d" )
215        
216        ## Info shortcuts
217        self.add_key_shortcut( "Info", shortname="measure length", fulltext="draw and measure a line length", key="Control"+"-i" )
218    
219    
220    def load_default_settings( self ):
221        """ Load all default widget settings """
222        self.settings = {}
223
224        ## Default visualisation set-up
225        self.settings["Display"] = {}
226        self.settings["Display"]["Layers"] = { 'Tracks': True, 'events': True, 'ROIs': False, 'Segmentation': True, 'Movie': True, 'EpicGrid': False, 'Groups': False }
227
228        self.settings["Info"] = {}
229
230        ## widgets colors
231        self.load_default_colors()
232
233        ## default visualisation of events widget
234        self.settings["events"] = {}
235
236    def load_default_colors( self ):
237        """ Load the defualt GUI colors """
238        self.settings["Display"]["Colors"] = {}
239        col_set = self.settings["Display"]["Colors"]
240        col_set["button"] = "rgb(40, 60, 75)"
241        col_set["Help button"] = "rgb(62, 60, 75)"
242        col_set["Reset button"] = "rgb(70, 68, 85)"
243        col_set["checkbox"] = "rgb(40, 52, 65)"
244        col_set["line edit"] = "rgb(30, 30, 40)"
245        col_set["group"] = "rgb(33,42,55)"
246        col_set["group4"] = "rgb(37,37,57)"
247        col_set["group3"] = "rgb(30,35,40)"
248        col_set["group2"] = "rgb(30,40,50)"
249        
250
251class ShortCut( QWidget ):
252    """ Class to handle edit EpiCure shortcuts """
253
254    def __init__( self, napari_viewer, pref ):
255        super().__init__()
256        
257        layout = QVBoxLayout()
258        self.ctl = "Control"
259        if platform.lower() == "darwin":
260            self.ctl = "Command"
261
262        self.sc = pref.get_shortcuts()
263        ## choice list to choose which shortcuts to edit
264        self.shortcut_types = self.sc.keys()
265        self.sc_types = QComboBox()
266        self.sc_groups = {}
267        self.sc_guis = {}
268        layout.addWidget( self.sc_types )
269        for sc_type in self.shortcut_types:
270            self.sc_types.addItem( sc_type )
271            self.sc_guis[sc_type] = {}
272            self.sc_groups[sc_type] = self.create_sc_type( sc_type )
273            layout.addWidget( self.sc_groups[sc_type] )
274        self.show_sc_type()
275
276        self.setLayout(layout)
277        self.sc_types.currentIndexChanged.connect( self.show_sc_type )
278
279    def show_sc_type( self ):
280        """ Show only selected shortcut subset """
281        for sc_type in self.shortcut_types:
282            self.sc_groups[ sc_type ].setVisible( self.sc_types.currentText() == sc_type )
283
284    def create_sc_type( self, sc_type ):
285        """ Interface to edit shortcut subset of a given type """
286        sc_curgroup = QGroupBox( "" )
287        sc_layout = QVBoxLayout()
288        
289        ## add each shortcut from the current selected group
290        cur_shortcuts = self.sc[ sc_type ]
291        for shortname, val in cur_shortcuts.items():
292            new_line = QHBoxLayout()
293            ## current keyboard shortcut
294            if val["type"] == "click":
295                ## shortcut is a mouse shortcut
296                if "modifiers" in val.keys():
297                    ind = 0
298                    for modif in val["modifiers"]:
299                        cur_modif = QComboBox()
300                        cur_modif.addItem("")
301                        cur_modif.addItem("Control")
302                        cur_modif.addItem("Shift")
303                        cur_modif.addItem("Alt")
304                        if platform.lower() == "darwin":
305                            cur_modif.addItem("Command")
306                            cur_modif.addItem("Option")
307                        new_line.addWidget( cur_modif )
308                        cur_modif.setCurrentText( modif )
309                        self.sc_guis[sc_type][ shortname+"modifiers"+str(ind) ] = cur_modif
310                        ind = ind + 1
311                cur_click = QComboBox()
312                cur_click.addItem("Left-click")
313                cur_click.addItem("Right-click")
314                new_line.addWidget( cur_click )
315                if val["button"] == "Right":
316                    cur_click.setCurrentText( "Right-click" )
317                self.sc_guis[sc_type][ shortname ] = cur_click
318            if val["type"] == "key":
319                new_line_val = QLineEdit()
320                new_line_val.setText( val["key"] )
321                self.sc_guis[sc_type][ shortname ] = new_line_val
322                new_line.addWidget( new_line_val )
323            ## full description of the shortcut
324            long_description = QLabel()
325            long_description.setText( val["text"] )
326            new_line.addWidget( long_description )
327            sc_layout.addLayout( new_line )
328            #empty = QLabel()
329            #sc_layout.addWidget( empty )
330        
331        sc_curgroup.setLayout( sc_layout )
332        return sc_curgroup
333
334    def update_pref( self ):
335        """ Update the shortcuts in the Preference based on current values """
336        for sc_type in self.shortcut_types:
337            gui = self.sc_guis[ sc_type ]
338            sc_group = self.sc[ sc_type ]
339            for shortname, vals in sc_group.items():
340                if vals["type"] == "click":
341                    ## update the modifiers if there are some
342                    ind = 0
343                    if "modifiers" in vals.keys():
344                        del vals["modifiers"]
345                    while shortname+"modifiers"+str(ind) in gui.keys():
346                        modif = gui[ shortname+"modifiers"+str(ind) ].currentText()
347                        if "modifiers" not in vals.keys():
348                            vals["modifiers"] = []
349                        if modif != "":
350                            vals["modifiers"].append(modif)
351                        ind = ind + 1
352                        if len( vals["modifiers"] ) == 0:
353                            del vals["modifiers"]
354                    ## update the button information
355                    click = gui[shortname].currentText()
356                    if click == "Left-click":
357                        vals["button"] = "Left"
358                    else:
359                        vals["button"] = "Right"
360                if vals["type"] == "key":
361                    vals["key"] = gui[shortname].text()
362
363
364class DisplaySettings( QWidget ):
365    """ Class to handle edit EpiCure display button colors...)"""
366    
367    def __init__( self, napari_viewer, pref ):
368        super().__init__()
369        self.settings = pref.get_settings()
370        if "Colors" not in self.settings["Display"]:
371            pref.load_default_colors()
372        colors = self.settings["Display"]["Colors"]
373
374        ## interface of display choices
375        layout = QVBoxLayout()
376        self.grid_color = QPushButton("EpicGrid color", self)
377        self.grid_color.clicked.connect( self.get_grid_color )
378        layout.addWidget( self.grid_color )
379
380        self.add_color( layout, "Buttons color", "button", "Choose default color of buttons" )
381        self.add_color( layout, "Help buttons color", "Help button", "Choose color of buttons for Help actions" )
382        self.add_color( layout, "Reset buttons color", "Reset button", "Choose color of buttons for Reset actions" )
383        self.add_color( layout, "CheckBox color", "checkbox", "Choose color of checkboxes" )
384        self.add_color( layout, "Input color", "line edit", "Choose color of editable parameters boxes" )
385        self.add_color( layout, "Subpanels color", "group", "Choose color of option subpanels that appears when clicked/selected" )
386        self.add_color( layout, "Subpanels color 2", "group2", "Choose second color of option subpanels that appears when clicked/selected" )
387        self.add_color( layout, "Subpanels color 3", "group3", "Choose third color of option subpanels that appears when clicked/selected" )
388        self.add_color( layout, "Subpanels color 4", "group4", "Choose fourth color of option subpanels that appears when clicked/selected" )
389        
390        self.setLayout(layout)
391
392    def add_color( self, layout, label, setname, descr="" ):
393        """ Add a choice of color (push button that opens a color dialog) """
394        btn = QPushButton( label )
395        if descr != "":
396            btn.setToolTip( descr )
397        def get_color():
398            """ opens color dialog and set button color to it """
399            color = QColorDialog.getColor()
400            if color.isValid():
401                self.settings["Display"]["Colors"][setname] = color.name()
402                btn.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )
403        btn.clicked.connect( get_color )
404        if setname in self.settings["Display"]["Colors"]:
405            color = self.settings["Display"]["Colors"][setname]
406            btn.setStyleSheet( 'QPushButton {background-color: '+color+'}' )
407        layout.addWidget( btn )
408
409    def get_grid_color( self ):
410        """ Get the EpiCGrid color """
411        color = QColorDialog.getColor()
412        if color.isValid():
413            if "Display" not in self.settings:
414                self.settings["Display"] = {}
415            self.settings["Display"]["Grid color"] = color.name()
416            self.grid_color.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )
417        
418            
def edit_preferences():
17def edit_preferences():
18    """ Launch preferences edition interface"""
19    viewer = napari.current_viewer()
20    prefgui = PreferencesGUI( viewer )
21    return prefgui

Launch preferences edition interface

class PreferencesGUI(PyQt6.QtWidgets.QWidget):
23class PreferencesGUI( QWidget ):
24    """ Handles user preferences for shortcuts, default widget state """
25
26    def __init__(self, napari_viewer):
27        """ Initialize the tab with the different widgets """
28        super().__init__()
29        
30        ## preferences (shortcuts, plugin state) object
31        self.pref = Preferences()
32        
33        layout = QVBoxLayout()
34        tabs = QTabWidget()
35        tabs.setObjectName("Preferences")
36        
37        ## shortcut and plugin state preferences tabs
38        self.shortcuts = ShortCut( napari_viewer, self.pref )
39        tabs.addTab( self.shortcuts, "Shortcuts Config." )
40        self.displays = DisplaySettings( napari_viewer, self.pref )
41        tabs.addTab( self.displays, "Display Config." )
42        layout.addWidget(tabs)
43
44        ## save option
45        self.save_pref = QPushButton("Save preferences", parent=self)
46        layout.addWidget( self.save_pref )
47        self.save_pref.clicked.connect( self.save )
48
49        ## add to main interface
50        self.setLayout( layout )
51        #napari_viewer.window.add_dock_widget( main_widget, name="Preferences" )
52
53    def save( self ):
54        """ Save current preferences: update them and save to default file """
55        self.shortcuts.update_pref()
56        self.pref.save()

Handles user preferences for shortcuts, default widget state

PreferencesGUI(napari_viewer)
26    def __init__(self, napari_viewer):
27        """ Initialize the tab with the different widgets """
28        super().__init__()
29        
30        ## preferences (shortcuts, plugin state) object
31        self.pref = Preferences()
32        
33        layout = QVBoxLayout()
34        tabs = QTabWidget()
35        tabs.setObjectName("Preferences")
36        
37        ## shortcut and plugin state preferences tabs
38        self.shortcuts = ShortCut( napari_viewer, self.pref )
39        tabs.addTab( self.shortcuts, "Shortcuts Config." )
40        self.displays = DisplaySettings( napari_viewer, self.pref )
41        tabs.addTab( self.displays, "Display Config." )
42        layout.addWidget(tabs)
43
44        ## save option
45        self.save_pref = QPushButton("Save preferences", parent=self)
46        layout.addWidget( self.save_pref )
47        self.save_pref.clicked.connect( self.save )
48
49        ## add to main interface
50        self.setLayout( layout )
51        #napari_viewer.window.add_dock_widget( main_widget, name="Preferences" )

Initialize the tab with the different widgets

pref
shortcuts
displays
save_pref
def save(self):
53    def save( self ):
54        """ Save current preferences: update them and save to default file """
55        self.shortcuts.update_pref()
56        self.pref.save()

Save current preferences: update them and save to default file

class Preferences:
 59class Preferences():
 60    """ Handles user-specific preferences (shortcuts, widgets states) """
 61    
 62    def __init__( self ):
 63        """ Initialise file path, load current preferences"""
 64        self.build_preferences_path()
 65        #print("Running on "+platform.lower())
 66        
 67        self.ctl = "Control"
 68        self.alt = "Alt"
 69        if platform.lower() == "darwin":
 70            self.ctl = "Command"
 71
 72        self.load_default_shortcuts()
 73        self.load_default_settings()
 74        if os.path.exists( self.preference_path ):
 75            self.load()
 76
 77    def build_preferences_path( self ):
 78        """ Build (create directories if necessary) preference path """
 79        home_dir = Path.home()
 80        self.preference_path = os.path.join( home_dir, ".napari" )
 81        if not os.path.exists( self.preference_path ):
 82            os.mkdir( self.preference_path )
 83        self.preference_path = os.path.join( self.preference_path, "epicure_preferences.pkl" )
 84
 85    def save( self ):
 86        """ Save the current preferences to the preference files in user home """
 87        outfile = open( self.preference_path, "wb" )
 88        pickle.dump( self.shortcuts, outfile )
 89        pickle.dump( self.settings, outfile )
 90        outfile.close()
 91        print( "Preferences saved in file "+self.preference_path )
 92
 93    def set_preferences( self, default_prefs, prefs ):
 94        """ Merge (recursively) the preferences with the default ones """
 95        for key, vals in prefs.items():
 96            if key in default_prefs.keys():
 97                if isinstance( vals, dict ):
 98                    self.set_preferences( default_prefs[key], vals )
 99                else:
100                    default_prefs[key] = vals
101            else:
102                default_prefs[key] = vals
103
104    def load( self ):
105       """ Load the current preferences to the preference files in user home """
106       infile = open( self.preference_path, "rb" )
107       shortcuts = pickle.load( infile )
108       self.set_preferences( self.shortcuts, shortcuts )
109       try:
110           settings = pickle.load( infile )
111           #print(settings)
112           self.set_preferences( self.settings, settings )
113           #print(self.settings)
114       except:
115           self.load_default_settings()
116       #print(self.shortcuts)
117       infile.close()
118       #print( "Preferences loaded from file "+self.preference_path )
119
120    def get_settings( self ):
121        """ Return the dict of prefered settings (widget state) """
122        return self.settings
123
124    def get_shortcuts( self ):
125        """ Return the dict of shortcuts """
126        return self.shortcuts
127
128    def add_key_shortcut( self, main_type, shortname, fulltext, key ):
129        """ Add a keyboard shortcut """
130        if main_type not in self.shortcuts.keys():
131            self.shortcuts[ main_type ] = {}
132        ## initialize the new shortcut object
133        self.shortcuts[ main_type ][ shortname ] = {}
134        sc = self.shortcuts[ main_type ][ shortname ]
135        sc["type"] = "key"
136        sc["text"] = fulltext
137        sc["key"] = key
138    
139    def add_click_shortcut( self, main_type, shortname, fulltext, button, modifiers=None ):
140        """ Add a keyboard shortcut """
141        if main_type not in self.shortcuts.keys():
142            self.shortcuts[ main_type ] = {}
143        ## initialize the new shortcut object
144        self.shortcuts[ main_type ][ shortname ] = {}
145        sc = self.shortcuts[ main_type ][ shortname ]
146        sc["type"] = "click"
147        sc["text"] = fulltext
148        sc["button"] = button
149        if modifiers is not None:
150            sc["modifiers"] = ["Control" if item=="Command" else item for item in modifiers] 
151
152
153    def load_default_shortcuts( self ):
154        """ Load all default shortcuts """
155
156        self.shortcuts = {}
157
158        ## General shortcuts
159        self.add_key_shortcut( "General", shortname="show help", fulltext="show/hide overlay help message", key="h" )
160        self.add_key_shortcut( "General", shortname="show all", fulltext="show all shortcuts in a separate window", key="a" )
161        self.add_key_shortcut( "General", shortname="save segmentation", fulltext="save the segmentation and epicure files", key="s" )
162        self.add_key_shortcut( "General", shortname="save movie", fulltext="save the movie with current display", key="Shift-s" )
163
164        ## Labels edition (static) shortcuts
165        self.add_key_shortcut( "Labels", shortname="unused paint", fulltext="set the current label to unused value and go to paint mode", key="n" )
166        self.add_key_shortcut( "Labels", shortname="unused fill", fulltext="set the current label to unused value and go to fill mode", key="Shift-n" )
167        self.add_key_shortcut( "Labels", shortname="swap mode", fulltext="then <"+self.ctl+">+Left click on one cell to another to swap their values", key="w" )
168
169        self.add_click_shortcut( "Labels", shortname="erase", fulltext="erase the cell under the click", button="Right", modifiers=None )
170        self.add_click_shortcut( "Labels", shortname="merge", fulltext="drag-click from one cell to another to merge them", button="Left", modifiers=["Control"] )
171        self.add_click_shortcut( "Labels", shortname="split accross", fulltext="drag-click in the cell to split into 2 cells ", button="Right", modifiers=["Control"] )
172        self.add_click_shortcut( "Labels", shortname="split draw", fulltext="drag-click draw a junction to split in 2 cells", button="Right", modifiers=["Alt"] )
173        self.add_click_shortcut( "Labels", shortname="redraw junction", fulltext="drag-click draw a junction to correct it", button="Left", modifiers=["Alt"] )
174        self.add_key_shortcut( "Labels", shortname="draw junction mode", fulltext="Draw junction(s) mode ON", key="j" )
175        self.add_click_shortcut( "Labels", shortname="drawing junction", fulltext="Draw junction mode ON. Drag-click draw a junction to create new cell(s)", button="Left", modifiers=["Control"] )
176        
177        ## Seeds (manual segmentation) shortcuts
178        self.add_key_shortcut( "Seeds", shortname="new seed", fulltext="<key shortcut> then left-click to place a seed", key="e" )
179        
180        ## Groups shortcuts
181        self.add_click_shortcut( "Groups", shortname="add group", fulltext="add the clicked cell to the current group", button="Left", modifiers=["Shift"] )
182        self.add_click_shortcut( "Groups", shortname="remove group", fulltext="remove the clicked cell from the group", button="Right", modifiers=["Shift"] )
183        
184        ## events edition shortcuts
185        self.add_key_shortcut( "Events", shortname="next", fulltext="zoom on next event", key="Space" )
186        self.add_click_shortcut( "Events", shortname="zoom", fulltext="Zoom on the clicked event", button="Left", modifiers=["Control", "Alt"] )
187        self.add_click_shortcut( "Events", shortname="delete", fulltext="Remove the clicked event", button="Right", modifiers=["Control", "Alt"] )
188        self.add_click_shortcut( "Events", shortname="add division", fulltext="add a division: drag-click from first to second daugther", button="Left", modifiers=["Control", "Shift"] )
189        self.add_click_shortcut( "Events", shortname="add extrusion", fulltext="add an extrusion: click on the last cell of the track", button="Right", modifiers=["Control", "Shift"] )
190
191        ## Tracks edition shortcuts
192        self.add_key_shortcut( "Tracks", shortname="show", fulltext="show/hide the tracks", key="r" )
193        self.add_key_shortcut( "Tracks", shortname="lineage color", fulltext="color the tracks by lineage", key="l" )
194        self.add_key_shortcut( "Tracks", shortname="mode", fulltext="on/off track editing mode", key="t" )
195        self.add_click_shortcut( "Tracks", shortname="merge first", fulltext="+track mode ON. Merge tracks: select the first", button="Left" )
196        self.add_click_shortcut( "Tracks", shortname="merge second", fulltext="+trackmode ON. Merge tracks: selec the second", button="Right" )
197        self.add_click_shortcut( "Tracks", shortname="split track", fulltext="+trackmode ON. Split the track temporally in 2", button="Right", modifiers=["Shift"] )
198        self.add_click_shortcut( "Tracks", shortname="start manual", fulltext="+trackmode ON. Start manual tracking, clicking on cells", button="Left", modifiers=["Control"] )
199        self.add_click_shortcut( "Tracks", shortname="end manual", fulltext="+trackmode ON. Finish manual tracking", button="Right", modifiers=["Control"] )
200        self.add_click_shortcut( "Tracks", shortname="interpolate first", fulltext="+trackmode ON. Interpolate temporally labels: select first", button="Left", modifiers=["Alt"] )
201        self.add_click_shortcut( "Tracks", shortname="interpolate second", fulltext="+trackmode ON. Interpolate temporally labels: select second", button="Right", modifiers=["Alt"] )
202        self.add_click_shortcut( "Tracks", shortname="swap", fulltext="+trackmode ON. Drag click to swap 2 tracks from current frame", button="Left", modifiers=["Shift"] )
203        self.add_click_shortcut( "Tracks", shortname="delete", fulltext="+trackmode ON. Delete all the track from current frame", button="Right", modifiers=["Control", "Alt"]  )
204
205        ## Visualisation option shortcuts
206        self.add_key_shortcut( "Display", shortname="vis. segmentation", fulltext="show/hide segmentation layer", key="b" )
207        self.add_key_shortcut( "Display", shortname="vis. movie", fulltext="show/hide movie layer", key="v" )
208        self.add_key_shortcut( "Display", shortname="vis. event", fulltext="show.hide events layer", key="x" )
209        self.add_key_shortcut( "Display", shortname="only movie", fulltext="show ONLY movie layer on/off", key="c" )
210        self.add_key_shortcut( "Display", shortname="light view", fulltext="on/off light segmentation view", key="d" )
211        self.add_key_shortcut( "Display", shortname="skeleton", fulltext="show/hide/update segmentation skeleton", key="k" )
212        self.add_key_shortcut( "Display", shortname="show side", fulltext="view layers side by side on/off", key="z" )
213        self.add_key_shortcut( "Display", shortname="grid", fulltext="show/hide grid", key="g" )
214        self.add_key_shortcut( "Display", shortname="increase", fulltext="increase label contour size", key="Control-c" )
215        self.add_key_shortcut( "Display", shortname="decrease", fulltext="decrease label contour size", key="Control-d" )
216        
217        ## Info shortcuts
218        self.add_key_shortcut( "Info", shortname="measure length", fulltext="draw and measure a line length", key="Control"+"-i" )
219    
220    
221    def load_default_settings( self ):
222        """ Load all default widget settings """
223        self.settings = {}
224
225        ## Default visualisation set-up
226        self.settings["Display"] = {}
227        self.settings["Display"]["Layers"] = { 'Tracks': True, 'events': True, 'ROIs': False, 'Segmentation': True, 'Movie': True, 'EpicGrid': False, 'Groups': False }
228
229        self.settings["Info"] = {}
230
231        ## widgets colors
232        self.load_default_colors()
233
234        ## default visualisation of events widget
235        self.settings["events"] = {}
236
237    def load_default_colors( self ):
238        """ Load the defualt GUI colors """
239        self.settings["Display"]["Colors"] = {}
240        col_set = self.settings["Display"]["Colors"]
241        col_set["button"] = "rgb(40, 60, 75)"
242        col_set["Help button"] = "rgb(62, 60, 75)"
243        col_set["Reset button"] = "rgb(70, 68, 85)"
244        col_set["checkbox"] = "rgb(40, 52, 65)"
245        col_set["line edit"] = "rgb(30, 30, 40)"
246        col_set["group"] = "rgb(33,42,55)"
247        col_set["group4"] = "rgb(37,37,57)"
248        col_set["group3"] = "rgb(30,35,40)"
249        col_set["group2"] = "rgb(30,40,50)"

Handles user-specific preferences (shortcuts, widgets states)

Preferences()
62    def __init__( self ):
63        """ Initialise file path, load current preferences"""
64        self.build_preferences_path()
65        #print("Running on "+platform.lower())
66        
67        self.ctl = "Control"
68        self.alt = "Alt"
69        if platform.lower() == "darwin":
70            self.ctl = "Command"
71
72        self.load_default_shortcuts()
73        self.load_default_settings()
74        if os.path.exists( self.preference_path ):
75            self.load()

Initialise file path, load current preferences

ctl
alt
def build_preferences_path(self):
77    def build_preferences_path( self ):
78        """ Build (create directories if necessary) preference path """
79        home_dir = Path.home()
80        self.preference_path = os.path.join( home_dir, ".napari" )
81        if not os.path.exists( self.preference_path ):
82            os.mkdir( self.preference_path )
83        self.preference_path = os.path.join( self.preference_path, "epicure_preferences.pkl" )

Build (create directories if necessary) preference path

def save(self):
85    def save( self ):
86        """ Save the current preferences to the preference files in user home """
87        outfile = open( self.preference_path, "wb" )
88        pickle.dump( self.shortcuts, outfile )
89        pickle.dump( self.settings, outfile )
90        outfile.close()
91        print( "Preferences saved in file "+self.preference_path )

Save the current preferences to the preference files in user home

def set_preferences(self, default_prefs, prefs):
 93    def set_preferences( self, default_prefs, prefs ):
 94        """ Merge (recursively) the preferences with the default ones """
 95        for key, vals in prefs.items():
 96            if key in default_prefs.keys():
 97                if isinstance( vals, dict ):
 98                    self.set_preferences( default_prefs[key], vals )
 99                else:
100                    default_prefs[key] = vals
101            else:
102                default_prefs[key] = vals

Merge (recursively) the preferences with the default ones

def load(self):
104    def load( self ):
105       """ Load the current preferences to the preference files in user home """
106       infile = open( self.preference_path, "rb" )
107       shortcuts = pickle.load( infile )
108       self.set_preferences( self.shortcuts, shortcuts )
109       try:
110           settings = pickle.load( infile )
111           #print(settings)
112           self.set_preferences( self.settings, settings )
113           #print(self.settings)
114       except:
115           self.load_default_settings()
116       #print(self.shortcuts)
117       infile.close()
118       #print( "Preferences loaded from file "+self.preference_path )

Load the current preferences to the preference files in user home

def get_settings(self):
120    def get_settings( self ):
121        """ Return the dict of prefered settings (widget state) """
122        return self.settings

Return the dict of prefered settings (widget state)

def get_shortcuts(self):
124    def get_shortcuts( self ):
125        """ Return the dict of shortcuts """
126        return self.shortcuts

Return the dict of shortcuts

def add_key_shortcut(self, main_type, shortname, fulltext, key):
128    def add_key_shortcut( self, main_type, shortname, fulltext, key ):
129        """ Add a keyboard shortcut """
130        if main_type not in self.shortcuts.keys():
131            self.shortcuts[ main_type ] = {}
132        ## initialize the new shortcut object
133        self.shortcuts[ main_type ][ shortname ] = {}
134        sc = self.shortcuts[ main_type ][ shortname ]
135        sc["type"] = "key"
136        sc["text"] = fulltext
137        sc["key"] = key

Add a keyboard shortcut

def add_click_shortcut(self, main_type, shortname, fulltext, button, modifiers=None):
139    def add_click_shortcut( self, main_type, shortname, fulltext, button, modifiers=None ):
140        """ Add a keyboard shortcut """
141        if main_type not in self.shortcuts.keys():
142            self.shortcuts[ main_type ] = {}
143        ## initialize the new shortcut object
144        self.shortcuts[ main_type ][ shortname ] = {}
145        sc = self.shortcuts[ main_type ][ shortname ]
146        sc["type"] = "click"
147        sc["text"] = fulltext
148        sc["button"] = button
149        if modifiers is not None:
150            sc["modifiers"] = ["Control" if item=="Command" else item for item in modifiers] 

Add a keyboard shortcut

def load_default_shortcuts(self):
153    def load_default_shortcuts( self ):
154        """ Load all default shortcuts """
155
156        self.shortcuts = {}
157
158        ## General shortcuts
159        self.add_key_shortcut( "General", shortname="show help", fulltext="show/hide overlay help message", key="h" )
160        self.add_key_shortcut( "General", shortname="show all", fulltext="show all shortcuts in a separate window", key="a" )
161        self.add_key_shortcut( "General", shortname="save segmentation", fulltext="save the segmentation and epicure files", key="s" )
162        self.add_key_shortcut( "General", shortname="save movie", fulltext="save the movie with current display", key="Shift-s" )
163
164        ## Labels edition (static) shortcuts
165        self.add_key_shortcut( "Labels", shortname="unused paint", fulltext="set the current label to unused value and go to paint mode", key="n" )
166        self.add_key_shortcut( "Labels", shortname="unused fill", fulltext="set the current label to unused value and go to fill mode", key="Shift-n" )
167        self.add_key_shortcut( "Labels", shortname="swap mode", fulltext="then <"+self.ctl+">+Left click on one cell to another to swap their values", key="w" )
168
169        self.add_click_shortcut( "Labels", shortname="erase", fulltext="erase the cell under the click", button="Right", modifiers=None )
170        self.add_click_shortcut( "Labels", shortname="merge", fulltext="drag-click from one cell to another to merge them", button="Left", modifiers=["Control"] )
171        self.add_click_shortcut( "Labels", shortname="split accross", fulltext="drag-click in the cell to split into 2 cells ", button="Right", modifiers=["Control"] )
172        self.add_click_shortcut( "Labels", shortname="split draw", fulltext="drag-click draw a junction to split in 2 cells", button="Right", modifiers=["Alt"] )
173        self.add_click_shortcut( "Labels", shortname="redraw junction", fulltext="drag-click draw a junction to correct it", button="Left", modifiers=["Alt"] )
174        self.add_key_shortcut( "Labels", shortname="draw junction mode", fulltext="Draw junction(s) mode ON", key="j" )
175        self.add_click_shortcut( "Labels", shortname="drawing junction", fulltext="Draw junction mode ON. Drag-click draw a junction to create new cell(s)", button="Left", modifiers=["Control"] )
176        
177        ## Seeds (manual segmentation) shortcuts
178        self.add_key_shortcut( "Seeds", shortname="new seed", fulltext="<key shortcut> then left-click to place a seed", key="e" )
179        
180        ## Groups shortcuts
181        self.add_click_shortcut( "Groups", shortname="add group", fulltext="add the clicked cell to the current group", button="Left", modifiers=["Shift"] )
182        self.add_click_shortcut( "Groups", shortname="remove group", fulltext="remove the clicked cell from the group", button="Right", modifiers=["Shift"] )
183        
184        ## events edition shortcuts
185        self.add_key_shortcut( "Events", shortname="next", fulltext="zoom on next event", key="Space" )
186        self.add_click_shortcut( "Events", shortname="zoom", fulltext="Zoom on the clicked event", button="Left", modifiers=["Control", "Alt"] )
187        self.add_click_shortcut( "Events", shortname="delete", fulltext="Remove the clicked event", button="Right", modifiers=["Control", "Alt"] )
188        self.add_click_shortcut( "Events", shortname="add division", fulltext="add a division: drag-click from first to second daugther", button="Left", modifiers=["Control", "Shift"] )
189        self.add_click_shortcut( "Events", shortname="add extrusion", fulltext="add an extrusion: click on the last cell of the track", button="Right", modifiers=["Control", "Shift"] )
190
191        ## Tracks edition shortcuts
192        self.add_key_shortcut( "Tracks", shortname="show", fulltext="show/hide the tracks", key="r" )
193        self.add_key_shortcut( "Tracks", shortname="lineage color", fulltext="color the tracks by lineage", key="l" )
194        self.add_key_shortcut( "Tracks", shortname="mode", fulltext="on/off track editing mode", key="t" )
195        self.add_click_shortcut( "Tracks", shortname="merge first", fulltext="+track mode ON. Merge tracks: select the first", button="Left" )
196        self.add_click_shortcut( "Tracks", shortname="merge second", fulltext="+trackmode ON. Merge tracks: selec the second", button="Right" )
197        self.add_click_shortcut( "Tracks", shortname="split track", fulltext="+trackmode ON. Split the track temporally in 2", button="Right", modifiers=["Shift"] )
198        self.add_click_shortcut( "Tracks", shortname="start manual", fulltext="+trackmode ON. Start manual tracking, clicking on cells", button="Left", modifiers=["Control"] )
199        self.add_click_shortcut( "Tracks", shortname="end manual", fulltext="+trackmode ON. Finish manual tracking", button="Right", modifiers=["Control"] )
200        self.add_click_shortcut( "Tracks", shortname="interpolate first", fulltext="+trackmode ON. Interpolate temporally labels: select first", button="Left", modifiers=["Alt"] )
201        self.add_click_shortcut( "Tracks", shortname="interpolate second", fulltext="+trackmode ON. Interpolate temporally labels: select second", button="Right", modifiers=["Alt"] )
202        self.add_click_shortcut( "Tracks", shortname="swap", fulltext="+trackmode ON. Drag click to swap 2 tracks from current frame", button="Left", modifiers=["Shift"] )
203        self.add_click_shortcut( "Tracks", shortname="delete", fulltext="+trackmode ON. Delete all the track from current frame", button="Right", modifiers=["Control", "Alt"]  )
204
205        ## Visualisation option shortcuts
206        self.add_key_shortcut( "Display", shortname="vis. segmentation", fulltext="show/hide segmentation layer", key="b" )
207        self.add_key_shortcut( "Display", shortname="vis. movie", fulltext="show/hide movie layer", key="v" )
208        self.add_key_shortcut( "Display", shortname="vis. event", fulltext="show.hide events layer", key="x" )
209        self.add_key_shortcut( "Display", shortname="only movie", fulltext="show ONLY movie layer on/off", key="c" )
210        self.add_key_shortcut( "Display", shortname="light view", fulltext="on/off light segmentation view", key="d" )
211        self.add_key_shortcut( "Display", shortname="skeleton", fulltext="show/hide/update segmentation skeleton", key="k" )
212        self.add_key_shortcut( "Display", shortname="show side", fulltext="view layers side by side on/off", key="z" )
213        self.add_key_shortcut( "Display", shortname="grid", fulltext="show/hide grid", key="g" )
214        self.add_key_shortcut( "Display", shortname="increase", fulltext="increase label contour size", key="Control-c" )
215        self.add_key_shortcut( "Display", shortname="decrease", fulltext="decrease label contour size", key="Control-d" )
216        
217        ## Info shortcuts
218        self.add_key_shortcut( "Info", shortname="measure length", fulltext="draw and measure a line length", key="Control"+"-i" )

Load all default shortcuts

def load_default_settings(self):
221    def load_default_settings( self ):
222        """ Load all default widget settings """
223        self.settings = {}
224
225        ## Default visualisation set-up
226        self.settings["Display"] = {}
227        self.settings["Display"]["Layers"] = { 'Tracks': True, 'events': True, 'ROIs': False, 'Segmentation': True, 'Movie': True, 'EpicGrid': False, 'Groups': False }
228
229        self.settings["Info"] = {}
230
231        ## widgets colors
232        self.load_default_colors()
233
234        ## default visualisation of events widget
235        self.settings["events"] = {}

Load all default widget settings

def load_default_colors(self):
237    def load_default_colors( self ):
238        """ Load the defualt GUI colors """
239        self.settings["Display"]["Colors"] = {}
240        col_set = self.settings["Display"]["Colors"]
241        col_set["button"] = "rgb(40, 60, 75)"
242        col_set["Help button"] = "rgb(62, 60, 75)"
243        col_set["Reset button"] = "rgb(70, 68, 85)"
244        col_set["checkbox"] = "rgb(40, 52, 65)"
245        col_set["line edit"] = "rgb(30, 30, 40)"
246        col_set["group"] = "rgb(33,42,55)"
247        col_set["group4"] = "rgb(37,37,57)"
248        col_set["group3"] = "rgb(30,35,40)"
249        col_set["group2"] = "rgb(30,40,50)"

Load the defualt GUI colors

class ShortCut(PyQt6.QtWidgets.QWidget):
252class ShortCut( QWidget ):
253    """ Class to handle edit EpiCure shortcuts """
254
255    def __init__( self, napari_viewer, pref ):
256        super().__init__()
257        
258        layout = QVBoxLayout()
259        self.ctl = "Control"
260        if platform.lower() == "darwin":
261            self.ctl = "Command"
262
263        self.sc = pref.get_shortcuts()
264        ## choice list to choose which shortcuts to edit
265        self.shortcut_types = self.sc.keys()
266        self.sc_types = QComboBox()
267        self.sc_groups = {}
268        self.sc_guis = {}
269        layout.addWidget( self.sc_types )
270        for sc_type in self.shortcut_types:
271            self.sc_types.addItem( sc_type )
272            self.sc_guis[sc_type] = {}
273            self.sc_groups[sc_type] = self.create_sc_type( sc_type )
274            layout.addWidget( self.sc_groups[sc_type] )
275        self.show_sc_type()
276
277        self.setLayout(layout)
278        self.sc_types.currentIndexChanged.connect( self.show_sc_type )
279
280    def show_sc_type( self ):
281        """ Show only selected shortcut subset """
282        for sc_type in self.shortcut_types:
283            self.sc_groups[ sc_type ].setVisible( self.sc_types.currentText() == sc_type )
284
285    def create_sc_type( self, sc_type ):
286        """ Interface to edit shortcut subset of a given type """
287        sc_curgroup = QGroupBox( "" )
288        sc_layout = QVBoxLayout()
289        
290        ## add each shortcut from the current selected group
291        cur_shortcuts = self.sc[ sc_type ]
292        for shortname, val in cur_shortcuts.items():
293            new_line = QHBoxLayout()
294            ## current keyboard shortcut
295            if val["type"] == "click":
296                ## shortcut is a mouse shortcut
297                if "modifiers" in val.keys():
298                    ind = 0
299                    for modif in val["modifiers"]:
300                        cur_modif = QComboBox()
301                        cur_modif.addItem("")
302                        cur_modif.addItem("Control")
303                        cur_modif.addItem("Shift")
304                        cur_modif.addItem("Alt")
305                        if platform.lower() == "darwin":
306                            cur_modif.addItem("Command")
307                            cur_modif.addItem("Option")
308                        new_line.addWidget( cur_modif )
309                        cur_modif.setCurrentText( modif )
310                        self.sc_guis[sc_type][ shortname+"modifiers"+str(ind) ] = cur_modif
311                        ind = ind + 1
312                cur_click = QComboBox()
313                cur_click.addItem("Left-click")
314                cur_click.addItem("Right-click")
315                new_line.addWidget( cur_click )
316                if val["button"] == "Right":
317                    cur_click.setCurrentText( "Right-click" )
318                self.sc_guis[sc_type][ shortname ] = cur_click
319            if val["type"] == "key":
320                new_line_val = QLineEdit()
321                new_line_val.setText( val["key"] )
322                self.sc_guis[sc_type][ shortname ] = new_line_val
323                new_line.addWidget( new_line_val )
324            ## full description of the shortcut
325            long_description = QLabel()
326            long_description.setText( val["text"] )
327            new_line.addWidget( long_description )
328            sc_layout.addLayout( new_line )
329            #empty = QLabel()
330            #sc_layout.addWidget( empty )
331        
332        sc_curgroup.setLayout( sc_layout )
333        return sc_curgroup
334
335    def update_pref( self ):
336        """ Update the shortcuts in the Preference based on current values """
337        for sc_type in self.shortcut_types:
338            gui = self.sc_guis[ sc_type ]
339            sc_group = self.sc[ sc_type ]
340            for shortname, vals in sc_group.items():
341                if vals["type"] == "click":
342                    ## update the modifiers if there are some
343                    ind = 0
344                    if "modifiers" in vals.keys():
345                        del vals["modifiers"]
346                    while shortname+"modifiers"+str(ind) in gui.keys():
347                        modif = gui[ shortname+"modifiers"+str(ind) ].currentText()
348                        if "modifiers" not in vals.keys():
349                            vals["modifiers"] = []
350                        if modif != "":
351                            vals["modifiers"].append(modif)
352                        ind = ind + 1
353                        if len( vals["modifiers"] ) == 0:
354                            del vals["modifiers"]
355                    ## update the button information
356                    click = gui[shortname].currentText()
357                    if click == "Left-click":
358                        vals["button"] = "Left"
359                    else:
360                        vals["button"] = "Right"
361                if vals["type"] == "key":
362                    vals["key"] = gui[shortname].text()

Class to handle edit EpiCure shortcuts

ShortCut(napari_viewer, pref)
255    def __init__( self, napari_viewer, pref ):
256        super().__init__()
257        
258        layout = QVBoxLayout()
259        self.ctl = "Control"
260        if platform.lower() == "darwin":
261            self.ctl = "Command"
262
263        self.sc = pref.get_shortcuts()
264        ## choice list to choose which shortcuts to edit
265        self.shortcut_types = self.sc.keys()
266        self.sc_types = QComboBox()
267        self.sc_groups = {}
268        self.sc_guis = {}
269        layout.addWidget( self.sc_types )
270        for sc_type in self.shortcut_types:
271            self.sc_types.addItem( sc_type )
272            self.sc_guis[sc_type] = {}
273            self.sc_groups[sc_type] = self.create_sc_type( sc_type )
274            layout.addWidget( self.sc_groups[sc_type] )
275        self.show_sc_type()
276
277        self.setLayout(layout)
278        self.sc_types.currentIndexChanged.connect( self.show_sc_type )
ctl
sc
shortcut_types
sc_types
sc_groups
sc_guis
def show_sc_type(self):
280    def show_sc_type( self ):
281        """ Show only selected shortcut subset """
282        for sc_type in self.shortcut_types:
283            self.sc_groups[ sc_type ].setVisible( self.sc_types.currentText() == sc_type )

Show only selected shortcut subset

def create_sc_type(self, sc_type):
285    def create_sc_type( self, sc_type ):
286        """ Interface to edit shortcut subset of a given type """
287        sc_curgroup = QGroupBox( "" )
288        sc_layout = QVBoxLayout()
289        
290        ## add each shortcut from the current selected group
291        cur_shortcuts = self.sc[ sc_type ]
292        for shortname, val in cur_shortcuts.items():
293            new_line = QHBoxLayout()
294            ## current keyboard shortcut
295            if val["type"] == "click":
296                ## shortcut is a mouse shortcut
297                if "modifiers" in val.keys():
298                    ind = 0
299                    for modif in val["modifiers"]:
300                        cur_modif = QComboBox()
301                        cur_modif.addItem("")
302                        cur_modif.addItem("Control")
303                        cur_modif.addItem("Shift")
304                        cur_modif.addItem("Alt")
305                        if platform.lower() == "darwin":
306                            cur_modif.addItem("Command")
307                            cur_modif.addItem("Option")
308                        new_line.addWidget( cur_modif )
309                        cur_modif.setCurrentText( modif )
310                        self.sc_guis[sc_type][ shortname+"modifiers"+str(ind) ] = cur_modif
311                        ind = ind + 1
312                cur_click = QComboBox()
313                cur_click.addItem("Left-click")
314                cur_click.addItem("Right-click")
315                new_line.addWidget( cur_click )
316                if val["button"] == "Right":
317                    cur_click.setCurrentText( "Right-click" )
318                self.sc_guis[sc_type][ shortname ] = cur_click
319            if val["type"] == "key":
320                new_line_val = QLineEdit()
321                new_line_val.setText( val["key"] )
322                self.sc_guis[sc_type][ shortname ] = new_line_val
323                new_line.addWidget( new_line_val )
324            ## full description of the shortcut
325            long_description = QLabel()
326            long_description.setText( val["text"] )
327            new_line.addWidget( long_description )
328            sc_layout.addLayout( new_line )
329            #empty = QLabel()
330            #sc_layout.addWidget( empty )
331        
332        sc_curgroup.setLayout( sc_layout )
333        return sc_curgroup

Interface to edit shortcut subset of a given type

def update_pref(self):
335    def update_pref( self ):
336        """ Update the shortcuts in the Preference based on current values """
337        for sc_type in self.shortcut_types:
338            gui = self.sc_guis[ sc_type ]
339            sc_group = self.sc[ sc_type ]
340            for shortname, vals in sc_group.items():
341                if vals["type"] == "click":
342                    ## update the modifiers if there are some
343                    ind = 0
344                    if "modifiers" in vals.keys():
345                        del vals["modifiers"]
346                    while shortname+"modifiers"+str(ind) in gui.keys():
347                        modif = gui[ shortname+"modifiers"+str(ind) ].currentText()
348                        if "modifiers" not in vals.keys():
349                            vals["modifiers"] = []
350                        if modif != "":
351                            vals["modifiers"].append(modif)
352                        ind = ind + 1
353                        if len( vals["modifiers"] ) == 0:
354                            del vals["modifiers"]
355                    ## update the button information
356                    click = gui[shortname].currentText()
357                    if click == "Left-click":
358                        vals["button"] = "Left"
359                    else:
360                        vals["button"] = "Right"
361                if vals["type"] == "key":
362                    vals["key"] = gui[shortname].text()

Update the shortcuts in the Preference based on current values

class DisplaySettings(PyQt6.QtWidgets.QWidget):
365class DisplaySettings( QWidget ):
366    """ Class to handle edit EpiCure display button colors...)"""
367    
368    def __init__( self, napari_viewer, pref ):
369        super().__init__()
370        self.settings = pref.get_settings()
371        if "Colors" not in self.settings["Display"]:
372            pref.load_default_colors()
373        colors = self.settings["Display"]["Colors"]
374
375        ## interface of display choices
376        layout = QVBoxLayout()
377        self.grid_color = QPushButton("EpicGrid color", self)
378        self.grid_color.clicked.connect( self.get_grid_color )
379        layout.addWidget( self.grid_color )
380
381        self.add_color( layout, "Buttons color", "button", "Choose default color of buttons" )
382        self.add_color( layout, "Help buttons color", "Help button", "Choose color of buttons for Help actions" )
383        self.add_color( layout, "Reset buttons color", "Reset button", "Choose color of buttons for Reset actions" )
384        self.add_color( layout, "CheckBox color", "checkbox", "Choose color of checkboxes" )
385        self.add_color( layout, "Input color", "line edit", "Choose color of editable parameters boxes" )
386        self.add_color( layout, "Subpanels color", "group", "Choose color of option subpanels that appears when clicked/selected" )
387        self.add_color( layout, "Subpanels color 2", "group2", "Choose second color of option subpanels that appears when clicked/selected" )
388        self.add_color( layout, "Subpanels color 3", "group3", "Choose third color of option subpanels that appears when clicked/selected" )
389        self.add_color( layout, "Subpanels color 4", "group4", "Choose fourth color of option subpanels that appears when clicked/selected" )
390        
391        self.setLayout(layout)
392
393    def add_color( self, layout, label, setname, descr="" ):
394        """ Add a choice of color (push button that opens a color dialog) """
395        btn = QPushButton( label )
396        if descr != "":
397            btn.setToolTip( descr )
398        def get_color():
399            """ opens color dialog and set button color to it """
400            color = QColorDialog.getColor()
401            if color.isValid():
402                self.settings["Display"]["Colors"][setname] = color.name()
403                btn.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )
404        btn.clicked.connect( get_color )
405        if setname in self.settings["Display"]["Colors"]:
406            color = self.settings["Display"]["Colors"][setname]
407            btn.setStyleSheet( 'QPushButton {background-color: '+color+'}' )
408        layout.addWidget( btn )
409
410    def get_grid_color( self ):
411        """ Get the EpiCGrid color """
412        color = QColorDialog.getColor()
413        if color.isValid():
414            if "Display" not in self.settings:
415                self.settings["Display"] = {}
416            self.settings["Display"]["Grid color"] = color.name()
417            self.grid_color.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )

Class to handle edit EpiCure display button colors...)

DisplaySettings(napari_viewer, pref)
368    def __init__( self, napari_viewer, pref ):
369        super().__init__()
370        self.settings = pref.get_settings()
371        if "Colors" not in self.settings["Display"]:
372            pref.load_default_colors()
373        colors = self.settings["Display"]["Colors"]
374
375        ## interface of display choices
376        layout = QVBoxLayout()
377        self.grid_color = QPushButton("EpicGrid color", self)
378        self.grid_color.clicked.connect( self.get_grid_color )
379        layout.addWidget( self.grid_color )
380
381        self.add_color( layout, "Buttons color", "button", "Choose default color of buttons" )
382        self.add_color( layout, "Help buttons color", "Help button", "Choose color of buttons for Help actions" )
383        self.add_color( layout, "Reset buttons color", "Reset button", "Choose color of buttons for Reset actions" )
384        self.add_color( layout, "CheckBox color", "checkbox", "Choose color of checkboxes" )
385        self.add_color( layout, "Input color", "line edit", "Choose color of editable parameters boxes" )
386        self.add_color( layout, "Subpanels color", "group", "Choose color of option subpanels that appears when clicked/selected" )
387        self.add_color( layout, "Subpanels color 2", "group2", "Choose second color of option subpanels that appears when clicked/selected" )
388        self.add_color( layout, "Subpanels color 3", "group3", "Choose third color of option subpanels that appears when clicked/selected" )
389        self.add_color( layout, "Subpanels color 4", "group4", "Choose fourth color of option subpanels that appears when clicked/selected" )
390        
391        self.setLayout(layout)
settings
grid_color
def add_color(self, layout, label, setname, descr=''):
393    def add_color( self, layout, label, setname, descr="" ):
394        """ Add a choice of color (push button that opens a color dialog) """
395        btn = QPushButton( label )
396        if descr != "":
397            btn.setToolTip( descr )
398        def get_color():
399            """ opens color dialog and set button color to it """
400            color = QColorDialog.getColor()
401            if color.isValid():
402                self.settings["Display"]["Colors"][setname] = color.name()
403                btn.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )
404        btn.clicked.connect( get_color )
405        if setname in self.settings["Display"]["Colors"]:
406            color = self.settings["Display"]["Colors"][setname]
407            btn.setStyleSheet( 'QPushButton {background-color: '+color+'}' )
408        layout.addWidget( btn )

Add a choice of color (push button that opens a color dialog)

def get_grid_color(self):
410    def get_grid_color( self ):
411        """ Get the EpiCGrid color """
412        color = QColorDialog.getColor()
413        if color.isValid():
414            if "Display" not in self.settings:
415                self.settings["Display"] = {}
416            self.settings["Display"]["Grid color"] = color.name()
417            self.grid_color.setStyleSheet( 'QPushButton {background-color: '+color.name()+'}' )

Get the EpiCGrid color