根目录 robotide 1、入口函数 __init__.py: def main(*args): noupdatecheck, debug_console, inpath = _parse_args(args) # 命令解析 返回参数 noupdatecheck, debug_console, inpath 都传入Ride def _parse_args(args): noupdatecheck = '--noupdatecheck' in args debug_console = '--debugconsole' in args _run(inpath, not noupdatecheck, debug_console) 入口函数 def _run(inpath=None, updatecheck=True, debug_console=False): # print(f"DEBUG: ENTER _run {inpath=}, {updatecheck=}, {debug_console=}") try: from robotide.application import RIDE # 主界面 from robotide.application import debugconsole # 调试后台 except ImportError: _show_old_wxpython_warning_if_needed() raise ride = RIDE(inpath, updatecheck) # 初始化 if wx.VERSION <= (4, 0, 4, '', ''): _show_old_wxpython_warning_if_needed(ride.frame) else: wx.CallAfter(_show_old_wxpython_warning_if_needed, ride.frame) if debug_console: wx.lib.inspection.InspectionTool().Show() debugconsole.start(ride) # 启动debugconsole ride.MainLoop() # 消息循环 robotide\application\application.py class RIDE(wx.App): def OnInit(self): # Overrides wx method # DEBUG To test RTL # self._initial_locale = wx.Locale(wx.LANGUAGE_ARABIC) self._initial_locale = wx.Locale(wx.LANGUAGE_ENGLISH_US) # Needed for SetToolTipString to work wx.HelpProvider.Set(wx.SimpleHelpProvider()) # DEBUG: adjust to wx versions # 配置读取 self.settings = RideSettings() # 数据库初始化 librarydatabase.initialize_database() # 配置 self.preferences = Preferences(self.settings) # 初始化 self.namespace = Namespace(self.settings) # 构建项目控制器 self._controller = Project(self.namespace, self.settings) # 构建RideFrame self.frame = RideFrame(self, self._controller) # DEBUG self.frame.Show() self._editor_provider = EditorProvider() self._plugin_loader = PluginLoader(self, self._get_plugin_dirs(), coreplugins.get_core_plugins()) self._plugin_loader.enable_plugins() perspective = self.settings.get('AUI Perspective', None) if perspective: self.frame.aui_mgr.LoadPerspective(perspective, True) try: nb_perspective = self.settings.get('AUI NB Perspective', None) if nb_perspective: self.frame.notebook.LoadPerspective(nb_perspective) except Exception as e: print(f"RIDE: There was a problem loading panels position." f" Please delete the definition 'AUI NB Perspective' in " f"{os.path.join(context.SETTINGS_DIRECTORY, 'settings.cfg')}") if not isinstance(e, IndexError): # If is with all notebooks disabled, continue raise e self.treeplugin = TreePlugin(self) if self.treeplugin.settings['_enabled']: self.treeplugin.register_frame(self.frame) self.fileexplorerplugin = FileExplorerPlugin(self, self._controller) if self.fileexplorerplugin.settings['_enabled']: self.fileexplorerplugin.register_frame(self.frame) if not self.treeplugin.opened: self.treeplugin.close_tree() # else: # wx.CallLater(200, self.treeplugin.populate, self.model) if not self.fileexplorerplugin.opened: self.fileexplorerplugin.close_tree() self.editor = self._get_editor() self.robot_version = self._find_robot_installation() # 加载数据------- self._load_data() self.treeplugin.populate(self.model) self.treeplugin.set_editor(self.editor) self._publish_system_info() self.frame.Show() # ###### DEBUG DANGER ZONE self.SetTopWindow(self.frame) self.frame.aui_mgr.Update() wx.CallLater(200, ReleaseNotes(self).bring_to_front) wx.CallLater(200, self.fileexplorerplugin.update_tree) if self._updatecheck: wx.CallAfter(UpdateNotifierController(self.settings).notify_update_if_needed, UpdateDialog) self.Bind(wx.EVT_ACTIVATE_APP, self.on_app_activate) PUBLISHER.subscribe(self.SetGlobalColour, RideSettingsChanged) PUBLISHER.subscribe(self.update_excludes, RideSettingsChanged) RideSettingsChanged(keys=('Excludes', 'init'), old=None, new=None).publish() return True class Namespace(object): def _init_caches(self): # 库 self._lib_cache = LibraryCache( self.settings, self.update, self._library_manager) # 资源 self._resource_factory = ResourceFactory(self.settings) # 数据文件 数据文件检索器 self._retriever = DatafileRetriever(self._lib_cache, self._resource_factory, self) self._context_factory = _RetrieverContextFactory() class LibraryCache(object): def __init__(self, settings, libraries_need_refresh_listener, library_manager): self._library_manager = None self._settings = settings if library_manager: self.set_library_manager(library_manager) #设置_library_manager self._libraries_need_refresh_listener = libraries_need_refresh_listener self._library_keywords = {} self.__default_libraries = None self.__default_kws = None class ResourceFactory(object): _IGNORE_RESOURCE_DIRECTORY_SETTING_NAME = 'ignored resource directory' def __init__(self, settings): self.cache = {} self.python_path_cache = {} self._excludes = settings.excludes self.check_path_from_excludes = self._excludes.contains # print("DEBUG: ResourceFactory init path_excludes %s\n" % self.check_path_from_excludes) class DatafileRetriever(object): def __init__(self, lib_cache, resource_factory, namespace): self._namespace = namespace self._lib_cache = lib_cache self._resource_factory = resource_factory # 关键字缓存 self.keyword_cache = ExpiringCache() #过期缓存 self._default_kws = None class Project(_BaseController, WithNamespace): def __init__(self, namespace=None, settings=None, library_manager=None): from .filecontrollers import ResourceFileControllerFactory self._library_manager = self._construct_library_manager(library_manager, settings) # 静态方法 构建库管理器 if not self._library_manager.is_alive(): self._library_manager.start() # 开启_library_manager self._name_space = namespace self._set_namespace(self._name_space) self.internal_settings = settings self._loader = DataLoader(self._name_space, settings) # 数据加载器 self.controller = None self.name = None self.external_resources = [] self._resource_file_controller_factory = ResourceFileControllerFactory(self._name_space, self) # 构建资源文件控制器工厂 self._serializer = Serializer(settings, LOG) # 序列化 @staticmethod def _construct_library_manager(library_manager, settings): # 'C:\\Users\\Administrator\\AppData\\Roaming\\RobotFramework\\ride\\librarykeywords.db' return library_manager or \ spec.LibraryManager(spec.DATABASE_FILE, SpecInitializer(settings.get('library xml directories', [])[:])) # 环境库地址 class LibraryManager(Thread): # 守护多线程 def __init__(self, database_name, spec_initializer=None): self._database_name = database_name self._database = None self._messages = queue.Queue() # 消息队列 self._spec_initializer = spec_initializer or SpecInitializer() Thread.__init__(self) self.daemon = True def run(self): self._initiate_database_connection() while True: try: if not self._handle_message(): break except Exception as err: msg = 'Library import handling threw an unexpected exception' RideLogException(message=msg, exception=err, level='WARN').publish() self._database.close() def _handle_message(self): # 库消息处理 message = self._messages.get() if not message: return False msg_type = message[0] if msg_type == 'fetch': # fetch self._handle_fetch_keywords_message(message) elif msg_type == 'insert': # insert self._handle_insert_keywords_message(message) elif msg_type == 'create': # create_database self._database.create_database() return True def _handle_insert_keywords_message(self, message): _, library_name, library_args, result_queue = message # message????何时传入 keywords = self._fetch_keywords(library_name, library_args) self._insert(library_name, library_args, keywords, lambda res: result_queue.put(res, timeout=3)) def _insert(self, library_name, library_args, keywords, callback): self._database.insert_library_keywords( # LibraryDatabase::insert_library_keywords library_name, library_args, keywords or []) self._call(callback, keywords) class DataLoader(object): def __init__(self, namespace, settings): self.namespace = namespace self.namespace.reset_resource_and_library_cache() # namespace 重置资源库缓存 self._settings = settings Namespace::def reset_resource_and_library_cache(self): self._init_caches() class ResourceFileControllerFactory(object): def __init__(self, namespace, project): self._resources = [] self._namespace = namespace self._project = project self._all_resource_imports_resolved = False class Serializer(object): def __init__(self, settings, logger): self._settings = settings self._logger = logger self._errors = [] class RideFrame(wx.Frame): def __init__(self, application, controller): size = application.settings.get('mainframe size', (1100, 700)) # DEBUG self.general_settings = application.settings['General'] wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, title='RIDE', pos=application.settings.get(MAINFRAME_POSITION, (50, 30)), size=size, style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.BORDER_THEME) # set Left to Right direction (while we don't have localization) self.SetLayoutDirection(wx.Layout_LeftToRight) # self.SetLayoutDirection(wx.Layout_RightToLeft) self.aui_mgr = aui.AuiManager(self) # tell AuiManager to manage this frame self.aui_mgr.SetManagedWindow(self) self.SetMinSize(wx.Size(400, 300)) self.ensure_on_screen() if application.settings.get(MAINFRAME_MAXIMIZED, False): self.Maximize() self._application = application self.controller = controller self._image_provider = ImageProvider() self.reformat = application.settings.get('reformat', False) self.general_settings = application.settings['General'] # .get_without_default('General') self.color_background_help = self.general_settings.get('background help', (240, 242, 80)) self.color_foreground_text = self.general_settings.get('foreground text', (7, 0, 70)) self.color_background = self.general_settings.get_without_default('background') self.color_foreground = self.general_settings.get_without_default('foreground') self.font_face = self.general_settings.get('font face', '') self.font_size = self.general_settings.get('font size', 11) self._init_ui() self._task_bar_icon = RIDETaskBarIcon(self, self._image_provider) self._plugin_manager = PluginManager(self.notebook) self._review_dialog = None self._view_all_tags_dialog = None self._current_external_dir = None self.Bind(wx.EVT_CLOSE, self.on_close) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.EVT_MOVE, self.on_move) self.Bind(wx.EVT_MAXIMIZE, self.on_maximize) self.Bind(wx.EVT_DIRCTRL_FILEACTIVATED, self.on_open_file) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.on_menu_open_file) self._subscribe_messages() wx.CallAfter(self.actions.register_tools) # DEBUG # DEBUG wx.CallAfter(self.OnSettingsChanged, self.general_settings) RIDE::def _load_data(self): self.workspace_path = self.workspace_path or self._get_latest_path() if self.workspace_path: # 更新当前默认项目地址 self._controller.update_default_dir(self.workspace_path) # 加载进度服务 observer = LoadProgressObserver(self.frame) self._controller.load_data(self.workspace_path, observer) -->Project::def load_data(self, path, load_observer=None): """ DEBUG: To be used in Localization from robotide.context import APP try: robot_version = APP.robot_version except AttributeError: robot_version = b'6.1.1' # It is failing at unit tests print(f"DEBUG: project.py Project ENTER robot version = {robot_version}") """ load_observer = load_observer or NullObserver() if self._load_initfile(path, load_observer): return if self._load_datafile(path, load_observer): return if self._load_resource(path, load_observer): return try: load_observer.error("Given file '%s' is not a valid Robot Framework " "test case or resource file." % path) except AttributeError: # DEBUG # print(f"DEBUG: load_data error not valid datafile: {path}") pass -->_load_initfile def _load_initfile(self, path, load_observer): if os.path.splitext(os.path.split(path)[1])[0] != '__init__': return None initfile = self._loader.load_initfile(path, load_observer) if not initfile: return None self._populate_from_datafile(path, initfile, load_observer) return initfile -->_load_datafile -->_load_resource OnInit--》 1、self.settings = RideSettings() 2、librarydatabase.initialize_database() 3、self.preferences = Preferences(self.settings) 4、self.namespace = Namespace(self.settings) --》 self._init_caches() # 缓存初始化--》 self._lib_cache = LibraryCache(self.settings, self.update, self._library_manager)--》 self._libraries_need_refresh_listener = libraries_need_refresh_listener==update self._library_keywords = {} self.__default_libraries = None self.__default_kws = None # 资源 self._resource_factory = ResourceFactory(self.settings)--》 self._excludes = settings.excludes self.check_path_from_excludes = self._excludes.contains # 数据文件 数据文件检索器 self._retriever = DatafileRetriever(self._lib_cache,self._resource_factory, self)--》 self._namespace = namespace self._lib_cache = lib_cache self._resource_factory = resource_factory self.keyword_cache = ExpiringCache() self._default_kws = None self._context_factory = _RetrieverContextFactory() self._set_pythonpath() PUBLISHER.subscribe(self._setting_changed, RideSettingsChanged) 5、self._controller = Project(self.namespace, self.settings) --》 1、开启_library_manager线程 self._library_manager = self._construct_library_manager(library_manager, settings) if not self._library_manager.is_alive(): self._library_manager.start() 2、self._set_namespace(self._name_space) namespace.set_library_manager(self._library_manager)--》 def set_library_manager(self, library_manager): self._library_manager = library_manager self._lib_cache.set_library_manager(library_manager)--》 self._library_manager = library_manager WithNamespace._set_namespace(self, namespace) 3、self._loader = DataLoader(self._name_space, settings) # 数据加载器 self.namespace.reset_resource_and_library_cache() self._init_caches() self._lib_cache = LibraryCache(self.settings, self.update, self._library_manager) # 资源 self._resource_factory = ResourceFactory(self.settings) # 数据文件 数据文件检索器 self._retriever = DatafileRetriever(self._lib_cache,self._resource_factory, self) self._context_factory = _RetrieverContextFactory() 4、self._resource_file_controller_factory = ResourceFileControllerFactory(self._name_space, self) self._resources = [] self._namespace = namespace self._project = project self._all_resource_imports_resolved = False 5、self._serializer = Serializer(settings, LOG) self._settings = settings self._logger = logger self._errors = [] # 创建 RideFrame 6、self.frame = RideFrame(self, self._controller) self.controller = controller self._subscribe_messages()