def setSetting(cls, key: str, value: Union[None, str, List, bool, int, float] ) -> bool: """ Saves a setting to addon's settings.xml A convenience method for Settings.setSetting(key + '.' + cls.provider, value) :param key: :param value: :return: """ if (not cls.isSettingSupported(key) and cls._logger.isEnabledFor(LazyLogger.WARNING)): cls._logger.warning('Setting: {}, not supported by voicing engine: {}' .format(key, cls.get_provider_name())) fully_qualified_key = key if key not in Settings.TOP_LEVEL_SETTINGS: fully_qualified_key = '{}.{}'.format(key, cls.provider) previous_value = Settings.getSetting(fully_qualified_key, None, setting_type=type(value)) changed = False if previous_value != value: changed = True Settings.setSetting(fully_qualified_key, value) return changed
def load_trailers() -> None: """ Start up the configured trailer discovery threads. Called whenever settings have changed to start any threads that have just ben enabled. :return: """ module_logger.enter() if Settings.get_include_library_trailers(): lib_instance = DiscoverLibraryMovies() lib_instance.discover_basic_information() # Manufacture trailer entries for folders which contain trailer # files. Note that files are assumed to be videos. if Settings.get_include_trailer_folders(): DiscoverFolderTrailers().discover_basic_information() if Settings.get_include_itunes_trailers(): DiscoverItunesMovies().discover_basic_information() if Settings.get_include_tmdb_trailers(): DiscoverTmdbMovies().discover_basic_information() if Settings.is_include_tfh_trailers(): DiscoverTFHMovies().discover_basic_information() Monitor.throw_exception_if_abort_requested(timeout=1.0) Monitor.set_startup_complete()
def selectSetting(cls, provider, setting, *args): import backends settingsList = backends.getSettingsList(provider, setting, *args) if not settingsList: xbmcgui.Dialog().ok(T(32182), T(32186)) return displays = [] for ID, display in settingsList: displays.append(display) # xbmcgui.Dialog().select((heading, list[, autoclose, preselect, # useDetails]) auto_close = -1 current_value = Settings.getSetting(setting) current_index = -1 if current_value is not None: current_index = displays.index(str(current_value)) idx = xbmcgui.Dialog().select(T(32187), displays, auto_close, current_index) if idx < 0: return choice = displays[idx] cls._logger.info('Setting {0} for {1} set to: {2}'.format( setting, provider, choice)) Settings.setSetting('{0}.{1}'.format(setting, provider), choice)
def startup_non_main_thread(): # type: () -> None """ :return: """ if module_logger.isEnabledFor(LazyLogger.DEBUG): module_logger.debug('%s', 'Enter', lazy_logger=False) Settings.save_settings() Monitor.register_settings_changed_listener( Settings.on_settings_changed) Monitor.register_settings_changed_listener( LazyLogger.on_settings_changed) try: Settings.get_locale() except AbortException: reraise(*sys.exc_info()) except Exception: pass load_trailers() # Start the periodic garbage collector CacheManager.get_instance().start_cache_garbage_collection_thread() Monitor.register_settings_changed_listener(load_trailers)
def __init__( self, key='', # type: str total_pages=0, # type: int query_by_year=False # type: bool ): # type: (...) -> None """ :param key: :param total_pages: """ self._logger = module_logger.getChild(type(self).__name__) self._number_of_unsaved_changes = 0 self._time_of_last_save = None self._key = key self._total_pages = total_pages self._total_pages_by_year = {} self._query_by_year = query_by_year self._years_to_query = None self._search_pages_configured = False self._logger.debug('remote_db_cache_path:', Settings.get_remote_db_cache_path()) self._path = os.path.join(Settings.get_remote_db_cache_path(), 'index', f'tmdb_{key}.json') self._temp_path = os.path.join(Settings.get_remote_db_cache_path(), 'index', f'tmdb_{key}.json.tmp') # type: self._cached_page_by_key: Optional[Dict[str, CachedPage]] = None
def checkNewVersion(self): try: # Fails on Helix beta 1 on OpenElec #1103 from distutils.version import LooseVersion except ImportError: def LooseVersion(v): comp = [int(x) for x in re.split(r'a|b', v)[0].split(".")] fourth = 2 fifth = 0 if 'b' in v: fourth = 1 fifth = int(v.split('b')[-1] or 0) elif 'a' in 'v': fourth = 0 fifth = int(v.split('a')[-1] or 0) comp.append(fourth) comp.append(fifth) return comp lastVersion = Settings.getSetting('version', '0.0.0') Settings.setSetting(Settings.VERSION, __version__) if lastVersion == '0.0.0': self.firstRun() return True elif LooseVersion(lastVersion) < LooseVersion(__version__): self.queueNotice('{0}... {1}'.format( Messages.get_msg(Messages.NEW_TTS_VERSION), __version__)) return True return False
def launch(): script_path = Constants.ADDON_PATH Settings.begin_configuring_settings() gui = SettingsDialog('script-tts-settings-dialog.xml', script_path, 'Default') gui.doModal()
def main(): run = sys.argv[1] env = None if sys.argv.__len__() == 3: env = sys.argv[2] try: settings = Settings() settings.load_env(env) settings.load_config(run) if run == 'alt': alt = Alt(settings.alt_config, settings.db_config) alt.update() elif run == 'proxy': proxy = Proxy(settings.alt_config, settings.proxy_config, settings.db_config) proxy.update() elif run == 'host': raise NotImplementedError else: logging.error('unknown run module') raise RuntimeError except RuntimeWarning: pass except NotImplementedError: pass
def preInstalledFirstRun(): if not SystemQueries.isPreInstalled( ): # Do as little as possible if there is no pre-install if SystemQueries.wasPreInstalled(): module_logger.info('PRE INSTALL: REMOVED') # Set version to 0.0.0 so normal first run will execute and fix the # keymap Settings.setSetting('version', '0.0.0') enabler.markPreOrPost() # Update the install status return False lastVersion = Settings.getSetting('version') if not enabler.isPostInstalled() and SystemQueries.wasPostInstalled(): module_logger.info('POST INSTALL: UN-INSTALLED OR REMOVED') # Add-on was removed. Assume un-installed and treat this as a # pre-installed first run to disable the addon elif lastVersion: enabler.markPreOrPost() # Update the install status return False # Set version to 0.0.0 so normal first run will execute on first enable Settings.setSetting('version', '0.0.0') module_logger.info('PRE-INSTALLED FIRST RUN') module_logger.info('Installing basic keymap') # Install keymap with just F12 enabling included from utils import keymapeditor keymapeditor.installBasicKeymap() module_logger.info('Pre-installed - DISABLING') enabler.disableAddon() return True
def load_cache(cls): # type: () -> None """ :return: """ path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'missing_tmdb_trailers.json') path = xbmcvfs.validatePath(path) cls.abort_on_shutdown() with cls.lock: try: parent_dir, file_name = os.path.split(path) DiskUtils.create_path_if_needed(parent_dir) if os.path.exists(path): with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: cls._all_missing_tmdb_trailers = json.load( cacheFile, encoding='utf-8', object_hook=TrailerUnavailableCache.datetime_parser) size = len(cls._all_missing_tmdb_trailers) Statistics.missing_tmdb_trailers_initial_size(size) except AbortException: reraise(*sys.exc_info()) except IOError as e: cls._logger.exception('') except JSONDecodeError as e: os.remove(path) except Exception as e: cls._logger.exception('') cls.abort_on_shutdown() path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'missing_library_trailers.json') path = xbmcvfs.validatePath(path) try: parent_dir, file_name = os.path.split(path) DiskUtils.create_path_if_needed(parent_dir) if os.path.exists(path): with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: cls._all_missing_library_trailers = json.load( cacheFile, encoding='utf-8', object_hook=TrailerUnavailableCache.datetime_parser) size = len(cls._all_missing_library_trailers) Statistics.missing_library_trailers_initial_size(size) except AbortException: reraise(*sys.exc_info()) except JSONDecodeError as e: os.remove(path) except IOError as e: cls._logger.exception('') except Exception as e: cls._logger.exception('') pass
def get_cached_json(cls, url, # type; str movie_id=None, # type: Union[str, int, None] error_msg=None, # type: Union[str, int, None] source=None, # type: Union[str, None] dump_results=False, # type: bool dump_msg='', # type: str headers=None, # type: Union[dict, None] params=None, # type: Union[dict, None] timeout=3.0 # type: int ): # type: (...) -> (int, str) """ Attempt to get cached JSON movie information before using the JSON calls to get it remotely. Any information not in the cache will be placed into it after successfully reading it. :param url: :param movie_id: :param error_msg: :param source: :param dump_results: :param dump_msg: :param headers: :param params: :param timeout: :return: """ if headers is None: headers = {} if params is None: params = {} trailer_data = None status = 0 if source is None or source not in Movie.LIB_TMDB_ITUNES_SOURCES: cls._logger.error('Invalid source:', source) if Settings.is_use_tmdb_cache(): trailer_data = Cache.read_tmdb_cache_json( movie_id, source, error_msg=error_msg) status = 0 if trailer_data is not None: trailer_data[Movie.CACHED] = True if trailer_data is None: status, trailer_data = JsonUtilsBasic.get_json(url, dump_results=dump_results, dump_msg=dump_msg, headers=headers, error_msg=error_msg, params=params, timeout=timeout) if ( status == 0 or status == 200) and trailer_data is not None and \ Settings.is_use_tmdb_cache(): Cache.write_tmdb_cache_json(movie_id, source, trailer_data) return status, trailer_data
def setSetting(self, key, value): if not self._configure_temp_settings_enabled: Settings.backend_changed(self.get_backend_class()) self._configure_temp_settings_enabled = True changed = self.get_backend_class().setSetting(key, value) if changed: self.settings_changed = True
def class_init(cls, screensaver: bool) -> None: cls._logger = module_logger.getChild(cls.__name__) Trace.enable_all() Settings.save_settings() cls._advanced_player = None cls._is_screensaver = screensaver cls._start_ui = None cls._callableTasks = queue.Queue(maxsize=0)
def is_more_discovery_needed(cls, movie: MovieType) -> bool: if movie[Movie.DISCOVERY_STATE] <= Movie.DISCOVERY_COMPLETE: return False more_discovery_needed = False title = movie[Movie.TITLE] try: normalized_trailer_path = movie.get(Movie.NORMALIZED_TRAILER) if normalized_trailer_path is None: normalized_trailer_path = '' cached_trailer_path = movie.get(Movie.CACHED_TRAILER) if cached_trailer_path is None: cached_trailer_path = '' if DiskUtils.is_url(movie.get(Movie.TRAILER, '')): # Remote Trailer if Settings.is_normalize_volume_of_downloaded_trailers(): try: if not os.path.exists(normalized_trailer_path): cls._logger.debug( f'title: {title} does not exist: ' f'{normalized_trailer_path}') movie[Movie.NORMALIZED_TRAILER] = None more_discovery_needed = True except Exception as e: cls._logger.log_exception(e) elif Settings.is_use_trailer_cache(): try: if not os.path.exists(cached_trailer_path): cls._logger.debug( f'title: {title} does not exist: ' f'{cached_trailer_path}') movie[Movie.CACHED_TRAILER] = None movie[Movie.NORMALIZED_TRAILER] = None more_discovery_needed = True except Exception as e: cls._logger.log_exception(e) elif Settings.is_normalize_volume_of_local_trailers(): # Local trailer try: if not os.path.exists(normalized_trailer_path): cls._logger.debug(f'title: {title} does not exist: ' f'{normalized_trailer_path}') movie[Movie.NORMALIZED_TRAILER] = None more_discovery_needed = True except Exception as e: cls._logger.log_exception(e) except Exception as e: cls._logger.log_exception() if more_discovery_needed: movie[Movie.DISCOVERY_STATE] = Movie.DISCOVERY_COMPLETE cls._logger.debug(f'More discovery needed: {title}') return more_discovery_needed
def reloadSettings(self): self.readerOn = not Settings.getSetting(Settings.READER_OFF, False) # OldLogger.reload() self.speakListCount = Settings.getSetting(Settings.SPEAK_LIST_COUNT, True) self.autoItemExtra = False if Settings.getSetting(Settings.AUTO_ITEM_EXTRA, False): self.autoItemExtra = Settings.getSetting( Settings.AUTO_ITEM_EXTRA_DELAY, 2)
def selectGenderSetting(cls, provider): auto_close = -1 # yesno(heading, message, nolabel, yeslabel, customlabel, autoclose]) choice = xbmcgui.Dialog().yesno(T(32211), T(32217), T(32212), T(32213), T(32211), auto_close) if choice: voice = 'female' else: voice = 'male' Settings.setSetting('{0}.{1}'.format('gender', provider), voice)
def set_backend(self, backend_id): if self.previous_backend != backend_id: self.get_backend_class(backend_id=backend_id) self.setSetting(Settings.BACKEND, backend_id) self.backend_instance = None self.engine = backend_id Settings.backend_changed(self.get_backend_class()) self.previous_backend = backend_id self.settings_changed = True self.backend_changed = True
def get_excluded_types(cls) -> {str}: ''' :return: ''' iso_639_2_name = Settings.get_lang_iso_639_2() iso_639_1_name = Settings.get_lang_iso_639_1() return { '- JP Sub', 'Interview', '- UK', '- BR Sub', '- FR', '- IT', '- AU', '- MX', '- MX Sub', '- BR', '- RU', '- DE', '- ES', '- FR Sub', '- KR Sub', '- Russian', '- French', '- Spanish', '- German', '- Latin American Spanish', '- Italian' }
def read_cached_value_from_disk(cls): # type: () -> CacheParameters """ :return: """ path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'tmdb_discovery_parameters.json') path = xbmcvfs.validatePath(path) parent_dir, file_name = os.path.split(path) if not os.path.exists(parent_dir): DiskUtils.create_path_if_needed(parent_dir) saved_preferences = None with CacheIndex.lock: try: if not os.access(path, os.R_OK): cls._logger.error( Messages.get_formatted_msg(Messages.CAN_NOT_READ_FILE, path)) return None file_mod_time = datetime.datetime.fromtimestamp( os.path.getmtime(path)) now = datetime.datetime.now() expiration_time = now - datetime.timedelta( Settings.get_expire_remote_db_cache_entry_days()) if file_mod_time < expiration_time: if cls._logger.isEnabledFor(LazyLogger.DEBUG): cls._logger.debug('cache file EXPIRED for:', path) return None Monitor.throw_exception_if_abort_requested() with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: saved_preferences = json.load(cacheFile, encoding='utf-8') saved_preferences = CacheParameters(saved_preferences) except AbortException: reraise(*sys.exc_info()) except IOError as e: cls._logger.exception('') exception_occurred = True except Exception as e: cls._logger.exception('') exception_occurred = True return saved_preferences
def on_settings_changed(self): # type: () -> None """ Rediscover trailers if the changed settings impacts this manager. """ clz = DiscoverLibraryMovies if clz.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): clz.logger.enter() try: if Settings.is_library_loading_settings_changed(): stop_thread = not Settings.get_include_library_trailers() self.restart_discovery(stop_thread) except Exception as e: clz.logger.exception('')
def on_settings_changed(self): # type: () -> None """ Rediscover trailers if the changed settings impacts this manager. By being here, TMDB discover is currently running. Only restart if there is a change. """ clz = DiscoverTFHMovies clz.logger.enter() if Settings.is_tfh_loading_settings_changed(): stop_thread = not Settings.is_include_tfh_trailers() self.restart_discovery(stop_thread)
def is_tmdb_id_missing_trailer(cls, tmdb_id): # type: (int) -> Union[bool, None] """ :param tmdb_id: :return: """ cls.abort_on_shutdown() with cls.lock: if tmdb_id not in cls._all_missing_tmdb_trailers: return None entry = cls._all_missing_tmdb_trailers[tmdb_id] elapsed_time = datetime.date.today() - entry['timestamp'] elapsed_days = elapsed_time.days if elapsed_days > Settings.get_expire_remote_db_trailer_check_days(): del cls._all_missing_tmdb_trailers[tmdb_id] entry = None cls.tmdb_cache_changed() if entry is None: Statistics.add_missing_tmdb_id_cache_miss() else: Statistics.add_missing_tmdb_cache_hit() return entry
def save_cache(cls): # type: () -> None """ :return: """ path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'tmdb_discovery_parameters.json') path = xbmcvfs.validatePath(path) parent_dir, file_name = os.path.split(path) if not os.path.exists(parent_dir): DiskUtils.create_path_if_needed(parent_dir) with CacheIndex.lock: try: Monitor.throw_exception_if_abort_requested() with io.open( path, mode='wt', newline=None, encoding='utf-8', ) as cacheFile: json_text = cls.to_json() cacheFile.write(json_text) cacheFile.flush() except AbortException: reraise(*sys.exc_info()) except (IOError) as e: cls._logger.exception('') except Exception as e: cls._logger.exception('')
def save_sound_file(cls, text_to_voice, # type: str suffix, # type: str voiced_text # type: bytes ): # type: (str, str, bytes) -> None """ Write the given voiced text to the cache """ path = VoiceCache.get_path_to_voice_file(text_to_voice, suffix) cache_directory = Settings.getSetting(Settings.CACHE_PATH, Constants.DEFAULT_CACHE_DIRECTORY) try: if not os.path.exists(cache_directory): try: os.makedirs(cache_directory) except Exception as e: cls._logger.error( 'Can not create cache directory: {}'.format(cache_directory)) if os.path.exists(path) and not os.access(path, os.W_OK): cls._logger.error('Can not write cache file: {}'.format(path)) with io.open(path, mode='wb') as cacheFile: cacheFile.write(voiced_text) cacheFile.flush() except Exception as e: cls._logger.error('Can not write cache file: {}'.format(path))
def setUpClass(cls): serino = "MDEVICE" # serino = "adede7a6" if len(sys.argv) > 1: serino = sys.argv[1] cls.mod = Media(serino, "Media") cls.set = Settings(cls.mod.device, "Settings")
def load_unprocessed_movie_cache(cls): # type: () -> None """ :return: """ path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'tmdb_unprocessed_movies.json') path = xbmcvfs.validatePath(path) try: parent_dir, file_name = os.path.split(path) DiskUtils.create_path_if_needed(parent_dir) with CacheIndex.lock: if os.path.exists(path): with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: cls._unprocessed_movies = json.load( cacheFile, encoding='utf-8', object_hook=CacheIndex.datetime_parser) cls.last_saved_movie_timestamp = None cls._unprocessed_movie_changes = 0 else: cls._unprocessed_movies = {} Monitor.throw_exception_if_abort_requested() except AbortException: reraise(*sys.exc_info()) except IOError as e: CacheIndex.logger().exception('') except JSONDecodeError as e: os.remove(path) except Exception as e: CacheIndex.logger().exception('')
def filter(certification: Certification) -> bool: ''' Checks whether a film's certification is within the configured allowable range of certifications. The configurable settings are: The maximum age/maturity of a film allowed. Are unrated films allowed. Are Not-Yet-Rated films allowed (for new/pre-release trailers). :param certification: :return: ''' passed = False # maximum_allowed_certification = Settings.get_rating_limit_setting() # if Rating._logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE): # Rating._logger.enter('rating:', rating, 'limit:', maximum_allowed_certification) if maximum_allowed_certification == Certification.UNRATED_RANK: # All films are allowed passed = True elif certification.get_rank() <= maximum_allowed_certification: passed = True elif certification.get_rank() == Certification.NOT_YET_RATED_RANK: passed = True return passed
def setUpClass(cls): serino = "MDEVICE" serino = "a541c694" if len(sys.argv)>1: serino = sys.argv[1] cls.mod = Message(serino, "Message") cls.set = Settings(cls.mod.device, "Settings")
def save_unprocessed_movie_cache(cls, flush=False): # type: (bool) -> None """ :param flush: :return: """ # TODO: Should use lock here, review locking with cls.lock: if cls._unprocessed_movie_changes == 0: return if (not flush and # Constants.TRAILER_CACHE_FLUSH_UPDATES) (cls._unprocessed_movie_changes < 10) and (datetime.datetime.now() - \ cls._last_saved_unprocessed_movie_timestamp) < datetime.timedelta(minutes=5)): return path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'tmdb_unprocessed_movies.json') path = xbmcvfs.validatePath(path) parent_dir, file_name = os.path.split(path) if not os.path.exists(parent_dir): DiskUtils.create_path_if_needed(parent_dir) try: with io.open( path, mode='wt', newline=None, encoding='utf-8', ) as cacheFile: # TODO: Need ability to interrupt when ABORT. Object_handler # not a valid arg to dumps json_text = json.dumps(cls.get_unprocessed_movies(), encoding='utf-8', ensure_ascii=False, default=CacheIndex.handler, indent=3, sort_keys=True) cacheFile.write(json_text) cacheFile.flush() cls._last_saved_unprocessed_movie_timestamp = datetime.datetime.now( ) cls._unprocessed_movie_changes = 0 Monitor.throw_exception_if_abort_requested() except AbortException: reraise(*sys.exc_info()) except IOError as e: cls.logger().exception('') except Exception as e: cls.logger().exception('')
def get_info(self, url: str) -> Tuple[int, Optional[List[MovieType]]]: """ Instead of downloading a video, get basic information about the video :param url: To get information from :return: a dictionary (MovieType) from the json returned by site """ clz = VideoDownloader trailer_info: Optional[List[MovieType]] = None if clz.check_too_many_requests(url) != 0: return 429, None info_logger = TfhInfoLogger(self, url, parse_json_as_youtube=False) try: ydl_opts = { 'forcejson': 'true', 'skip_download': 'true', 'logger': info_logger, 'progress_hooks': [TrailerInfoProgressHook(self).status_hook] } cookie_path = Settings.get_youtube_dl_cookie_path() if len(cookie_path) > 0 and os.path.exists(cookie_path): ydl_opts['cookiefile'] = cookie_path Monitor.throw_exception_if_abort_requested() # Start Download with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) # Wait for download while info_logger.is_finished is None and self._error == 0: Monitor.throw_exception_if_abort_requested(timeout=0.5) if self._error == 0: trailer_info: List[MovieType] = info_logger.get_trailer_info() except AbortException: reraise(*sys.exc_info()) except Exception as e: if self._error == 0: clz._logger.exception(e) if clz._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): clz._logger.debug_verbose( 'Failed to download site info for:', url) trailer_info = None if self._error not in (0, 99): clz._logger.debug('Results for url:', url, 'error:', self._error) info_logger.log_debug() info_logger.log_warning() info_logger.log_error() Monitor.throw_exception_if_abort_requested(timeout=DELAY) return 0, trailer_info
def run(self): # type: () -> None """ :return: """ local_class = DiscoverFolderTrailers if local_class.logger.isEnabledFor(LazyLogger.DEBUG): try: import resource memory = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss local_class.logger.debug(': memory: ' + str(memory)) except ImportError: pass start_time = datetime.datetime.now() try: finished = False while not finished: try: self.discover_basic_information_worker( Settings.get_trailers_paths()) self.wait_until_restart_or_shutdown() except RestartDiscoveryException: # Restart discovery if local_class.logger.isEnabledFor(LazyLogger.DEBUG): local_class.logger.debug('Restarting discovery') self.prepare_for_restart_discovery() if not Settings.get_include_trailer_folders(): finished = True self.remove_self() except AbortException: return # Just exit thread except Exception: local_class.logger.exception('') self.finished_discovery() duration = datetime.datetime.now() - start_time if local_class.logger.isEnabledFor(LazyLogger.DEBUG): local_class.logger.debug('Time to discover:', duration.seconds, 'seconds', trace=Trace.STATS)
def main(): app = QtGui.QApplication(sys.argv) vim_controller_settings = Settings() vim_controller_settings.file_path_double_clicked = file_path_double_clicked vim_controller_settings.line_nr_double_clicked = line_nr_double_clicked file_tree_widget = FileTreeWidget(vim_controller_settings) grep_widget = GrepWidget(vim_controller_settings) build_widget = BuildWidget(vim_controller_settings) window = MainWindow("Vim Controller", vim_controller_settings) window.add_widget("Files", file_tree_widget) window.add_widget("Grep", grep_widget) window.add_widget("Build", build_widget) window.show() sys.exit(app.exec_())