1515
1616import builtins
1717import os
18+ import psutil
1819import subprocess
20+ import sys
1921
2022import wx
2123
2224from contextlib import contextmanager
2325from pathlib import Path
2426
2527from ..namespace import Namespace
28+ from ..context import SETTINGS_DIRECTORY
2629from ..controller import Project
2730from ..spec import librarydatabase
2831from ..ui import LoadProgressObserver
5861FOREGROUND_TEXT = 'foreground text'
5962FONT_SIZE = 'font size'
6063FONT_FACE = 'font face'
64+ SPC = " "
65+ FIVE_SPC = SPC * 5
66+ NORMAL_SETTINGS = _ ('Global Settings' )
67+ NORMAL_SETTINGS_DETECTED = _ ('Global Settings Detected' )
68+ PROJECT_SETTINGS = _ ('Project Settings' )
69+ PROJECT_SETTINGS_DETECTED = _ ('Project Settings Detected' )
70+
71+
72+ def restart_RIDE (args :list ):
73+ script = os .path .abspath (os .path .dirname (__file__ ) + '/../__init__.py' ) # This is probably a different signature
74+ python = sys .executable
75+ arguments = [python , script ]
76+ for a in args :
77+ if a :
78+ arguments .append (a )
79+ # print(f"DEBUG: Application.py restart_RIDE arguments={arguments}\n")
80+ """
81+ try:
82+ process = psutil.Process(os.getpid())
83+ # process.terminate()
84+ for handler in process.open_files() + process.connections():
85+ os.close(handler.fd)
86+ except Exception as e:
87+ pass
88+ """
89+ str_args = " " .join (arguments )
90+ subprocess .Popen (str_args , shell = True )
6191
6292
6393class UnthemableWidgetError (Exception ):
@@ -84,8 +114,10 @@ def __init__(self, path=None, updatecheck=True, settingspath=None):
84114 self ._updatecheck = updatecheck
85115 self .workspace_path = path
86116 self .changed_workspace = False
117+ self .preferences = None
87118 self .settings_path = settingspath
88119 context .APP = self
120+ # print(f"DEBUG: Application.py RIDE __init__ path={self.workspace_path}\n")
89121 wx .App .__init__ (self , redirect = False )
90122
91123 def OnInit (self ): # Overrides wx method
@@ -94,6 +126,16 @@ def OnInit(self): # Overrides wx method
94126 self ._locale = wx .Locale (wx .LANGUAGE_ENGLISH_US ) # LANGUAGE_PORTUGUESE
95127 # Needed for SetToolTipString to work
96128 wx .HelpProvider .Set (wx .SimpleHelpProvider ()) # DEBUG: adjust to wx versions
129+ # Use Project settings if available
130+ from ..recentfiles import RecentFilesPlugin
131+ self .settings = RideSettings (self .settings_path ) # We need this to know the available plugins
132+ self ._plugin_loader = PluginLoader (self , self ._get_plugin_dirs (), [RecentFilesPlugin ],
133+ silent = True )
134+ # print(f"DEBUG: Application.py RIDE OnInit path={self.workspace_path}\n")
135+ self .workspace_path = self .workspace_path or self ._get_latest_path ()
136+ # print(f"DEBUG: Application.py RIDE OnInit AFTER _get_latest_path() path={self.workspace_path}")
137+ if not self .settings_path :
138+ self .settings_path = self .initialize_project_settings (self .workspace_path )
97139 self .settings = RideSettings (self .settings_path )
98140
99141 class Message :
@@ -105,7 +147,8 @@ class Message:
105147 from ..context import coreplugins , SETTINGS_DIRECTORY
106148 from ..ui .treeplugin import TreePlugin
107149 librarydatabase .initialize_database ()
108- self .preferences = Preferences (self .settings )
150+ # self.preferences = Preferences(self.settings, self.settings_path)
151+ self .reload_preferences (Message )
109152 self .namespace = Namespace (self .settings )
110153 self ._controller = Project (self .namespace , self .settings )
111154 # Try to get FontInfo as soon as possible
@@ -115,8 +158,7 @@ class Message:
115158 self .frame = RideFrame (self , self ._controller )
116159 # DEBUG self.frame.Show()
117160 self ._editor_provider = EditorProvider ()
118- self ._plugin_loader = PluginLoader (self , self ._get_plugin_dirs (),
119- coreplugins .get_core_plugins ())
161+ self ._plugin_loader = PluginLoader (self , self ._get_plugin_dirs (), coreplugins .get_core_plugins ())
120162 self ._plugin_loader .enable_plugins ()
121163 perspective = self .settings .get ('AUI Perspective' , None )
122164 if perspective :
@@ -128,7 +170,7 @@ class Message:
128170 except Exception as e :
129171 print (f"RIDE: There was a problem loading panels position."
130172 f" Please delete the definition 'AUI NB Perspective' in "
131- f"{ os .path .join (SETTINGS_DIRECTORY , 'settings.cfg' )} " )
173+ f"{ self . settings_path or os .path .join (SETTINGS_DIRECTORY , 'settings.cfg' )} " )
132174 if not isinstance (e , IndexError ): # If is with all notebooks disabled, continue
133175 raise e
134176 self .fileexplorerplugin = FileExplorerPlugin (self , self ._controller )
@@ -155,15 +197,69 @@ class Message:
155197 RideSettingsChanged (keys = ('Excludes' , 'init' ), old = None , new = None ).publish ()
156198 PUBLISHER .subscribe (self .change_locale , RideSettingsChanged )
157199 RideSettingsChanged (keys = ('General' , 'ui language' ), old = None , new = None ).publish ()
200+ PUBLISHER .subscribe (self .reload_preferences , RideSettingsChanged )
158201 wx .CallLater (600 , ReleaseNotes (self ).bring_to_front )
159202 return True
160203
161204 def OnExit (self ):
162205 PUBLISHER .unsubscribe_all ()
206+ self .frame .on_exit (None )
163207 self .Destroy ()
164208 wx .Exit ()
165209 return True
166210
211+ def reload_preferences (self , message ):
212+ if message .keys [0 ] != "General" :
213+ return
214+ if self .preferences :
215+ del self .preferences
216+ if self .settings_path :
217+ self .settings = RideSettings (self .settings_path )
218+ self .preferences = Preferences (self .settings , self .settings_path )
219+ try :
220+ if message .keys [1 ] in ["reload" , "restore" ]:
221+ # reload plugins (was the original idea)
222+ if message .keys [1 ] == "reload" :
223+ project_or_normal = PROJECT_SETTINGS
224+ project_or_normal_detected = PROJECT_SETTINGS_DETECTED
225+ else :
226+ project_or_normal = NORMAL_SETTINGS
227+ project_or_normal_detected = NORMAL_SETTINGS_DETECTED
228+
229+ from .updatenotifier import _askyesno
230+ if not _askyesno (_ ("Restart RIDE?" ),
231+ f"{ FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } "
232+ f"{ project_or_normal_detected } { FIVE_SPC } \n \n "
233+ f"{ FIVE_SPC } { _ ('RIDE must be restarted to fully use these ' )} { project_or_normal } "
234+ f"{ FIVE_SPC } \n " f"\n \n { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } "
235+ f"{ _ ('Click OK to Restart RIDE!' )} { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } { FIVE_SPC } "
236+ f"\n \n " , wx .GetActiveWindow (), no_default = False ):
237+ return False
238+ args = [f"{ '--noupdatecheck' if not self ._updatecheck else '' } " ,
239+ f"--settingspath { message .keys [2 ]} " , f"{ message .keys [3 ]} " ]
240+ restart_RIDE (args )
241+ wx .CallLater (1000 , self .OnExit )
242+ # The next block was an attempt to reload plugins, in particular Test Runner to
243+ # load the Arguments for the project. This did not work because the plugins are
244+ # loaded at creation of ui/mainframe. For now, we open a new instance of RIDE
245+ # with the test suite/project argument.
246+ """
247+ # print("DEBUG: application.py RELOAD PLUGINS HERE!")
248+ from time import sleep
249+ for plu in self._plugin_loader.plugins:
250+ # print(f"DEBUG: Application RIDE plugin: {plu} {plu.name}\n "
251+ # f"Plugin object={plu.conn_plugin}")
252+ if plu.name == 'Editor':
253+ plu.conn_plugin._show_editor()
254+ if plu.name == 'Test Runner':
255+ plu.disable()
256+ sleep(2)
257+ plu.enable()
258+ # plu.conn_plugin._show_notebook_tab()
259+ """
260+ except IndexError :
261+ pass
262+
167263 @staticmethod
168264 def _ApplyThemeToWidget (widget , fore_color = wx .BLUE , back_color = wx .LIGHT_GREY , theme : (None , dict ) = None ):
169265 if theme is None :
@@ -335,7 +431,6 @@ def _load_data(self):
335431 theme = self .settings .get_without_default ('General' )
336432 background = theme ['background' ]
337433 foreground = theme ['foreground' ]
338- # print(f"DEBUG: application.py RIDE _load_data CALL PROGRESS {background=} {foreground=}")
339434 observer = LoadProgressObserver (self .frame , background = background , foreground = foreground )
340435 self ._controller .load_data (self .workspace_path , observer )
341436
@@ -363,10 +458,18 @@ def _find_robot_installation():
363458 return None
364459
365460 def _get_latest_path (self ):
461+ last_file = None
462+ last_file_path = os .path .join (SETTINGS_DIRECTORY , 'last_file.txt' )
463+ if os .path .isfile (last_file_path ):
464+ with open (last_file_path , 'r' , encoding = 'utf-8' ) as fp :
465+ last_file = fp .read ()
366466 recent = self ._get_recentfiles_plugin ()
367467 if not recent or not recent .recent_files :
368- return None
369- return recent .recent_files [0 ]
468+ if last_file :
469+ return last_file
470+ else :
471+ return None
472+ return last_file if last_file and last_file != recent .recent_files [0 ] else recent .recent_files [0 ]
370473
371474 def _get_recentfiles_plugin (self ):
372475 from ..recentfiles import RecentFilesPlugin
@@ -377,6 +480,49 @@ def _get_recentfiles_plugin(self):
377480 def get_plugins (self ):
378481 return self ._plugin_loader .plugins
379482
483+ def initialize_project_settings (self , path ):
484+ """ Detects if exists a directory named .robot at project root, and creates ride_settings.cfg
485+ if not exists, or returns the path to existing file.
486+ """
487+ from ..preferences import initialize_settings
488+ default_dir = path if os .path .isdir (path ) else os .path .dirname (path )
489+ local_settings_dir = os .path .join (default_dir , '.robot' )
490+ old_settings_dir = self .settings_path
491+ # print(f"DEBUG: Project.py Project initialize_project_settings ENTER: path={local_settings_dir}")
492+ if os .path .isdir (local_settings_dir ):
493+ # old_settings = self.internal_settings.get_without_default('General')
494+ # old_bkg = old_settings['background']
495+ local_settings = os .path .join (local_settings_dir , 'ride_settings.cfg' )
496+ self .settings_path = local_settings
497+ if os .path .isfile (local_settings ):
498+ # os.putenv('RIDESETTINGS', local_settings)
499+ os .environ ['RIDESETTINGS' ] = local_settings
500+ self .settings = RideSettings (local_settings )
501+ # print(f"DEBUG: Project.py Project initialize_project_settings EXISTING project settings "
502+ # f"{local_settings=} \nRIDESETTINGS={os.environ['RIDESETTINGS']}"
503+ # f"\nsettings={self.dump_settings()}")
504+ else :
505+ default = RideSettings ()
506+ settings_path = default .user_path
507+ new_path = initialize_settings (path = settings_path , dest_file_name = local_settings )
508+ # print(f"DEBUG: Project.py Project initialize_project_settings NEW project settings new_path={new_path}"
509+ # f" local_settings={local_settings}")
510+ self .settings = RideSettings (new_path )
511+ # new_settings = self.settings.get_without_default('General')
512+ # new_bkg = new_settings.get_without_default('background')
513+ # RideSettingsChanged(keys=('General', 'background'), old=old_bkg, new=new_bkg).publish()
514+ else :
515+ os .environ ['RIDESETTINGS' ] = ''
516+ # print(f"DEBUG: Project.py Project initialize_project_settings RETURNING: path={self.settings_path}")
517+ if self .settings_path != old_settings_dir :
518+ RideSettingsChanged (keys = ('General' , ), old = None , new = None ).publish ()
519+ return self .settings_path
520+
521+ def dump_settings (self ):
522+ keys = [items for items in self .settings ]
523+ values = [f"{ val } ={ self .settings [val ]} " for val in keys ]
524+ return values
525+
380526 def register_preference_panel (self , panel_class ):
381527 """Add the given panel class to the list of known preference panels"""
382528 self .preferences .add (panel_class )
0 commit comments