def core(progress): # setup mover thread mover = files.move_folder_thread(old_install, new_install) mover.activity_update.connect(progress.set_activity) def failed(err): typ = type(err) message = err logger.exception("Failed to move folder") error_dialogs.general(self, typ, message) # start the thread with thread.thread_wait( mover.finished, failed_signal=mover.failed, failed_func=failed, update_signal=mover.activity_update, ): mover.start() config.set_key_value(config.MOD_INSTALL_FOLDER_KEY, new_install, path=True) information_dialogs.disabled_mods_folder(self, new_install)
def user_selection(): """Function to keep user in a loop until they select correct folder.""" # prompt user to select self.sim_folder = QtWidgets.QFileDialog.getExistingDirectory( parent=self, caption="Select the root Microsoft Flight Simulator directory", dir=os.getenv("APPDATA"), ) if not self.sim_folder.strip(): sys.exit() elif flight_sim.is_sim_packages_folder(self.sim_folder): # save the config file config.set_key_value(config.SIM_FOLDER_KEY, self.sim_folder) else: # show error QtWidgets.QMessageBox().warning( self, "Error", "Invalid Microsoft Flight Simulator path." + " Please select the Packages folder manually" + " (which contains the Official and Community folders).", ) # send them through again user_selection()
def find_sim(self): """Sets the path to the simulator root folder.""" def user_selection(): """Function to keep user in a loop until they select correct folder.""" # prompt user to select self.sim_folder = QtWidgets.QFileDialog.getExistingDirectory( parent=self, caption="Select the root Microsoft Flight Simulator directory", dir=os.getenv("APPDATA"), ) if not self.sim_folder.strip(): sys.exit() elif flight_sim.is_sim_packages_folder(self.sim_folder): # save the config file config.set_key_value(config.SIM_FOLDER_KEY, self.sim_folder) else: # show error QtWidgets.QMessageBox().warning( self, "Error", "Invalid Microsoft Flight Simulator path." + " Please select the Packages folder manually" + " (which contains the Official and Community folders).", ) # send them through again user_selection() # try to automatically find the sim ( success, self.sim_folder, ) = flight_sim.find_sim_folder() if not self.sim_folder: # show error QtWidgets.QMessageBox().warning( self, "Error", "Microsoft Flight Simulator path could not be found." + " Please select the Packages folder manually" + " (which contains the Official and Community folders).", ) user_selection() elif not success: # save the config file config.set_key_value(config.SIM_FOLDER_KEY, self.sim_folder) # notify user QtWidgets.QMessageBox().information( self, "Info", "Your Microsoft Flight Simulator folder path was automatically detected to {}" .format(self.sim_folder), )
def get_last_open_folder() -> str: """Gets the last opened directory from the config file.""" succeeded, value = config.get_key_value(config.LAST_OPEN_FOLDER_KEY, path=True) if not succeeded or not os.path.isdir(value): # if mod install folder could not be loaded from config value = os.path.abspath( os.path.join(os.path.expanduser("~"), "Downloads")) config.set_key_value(config.LAST_OPEN_FOLDER_KEY, value, path=True) return fix_path(value)
def check_version(self): """Checks the application version and allows user to open browser to update.""" installed = version.is_installed() return_url = version.check_version(self.appctxt, installed) if return_url: result, remember = version_check_widget(self, installed).exec_() if result == QtWidgets.QMessageBox.Yes: if installed: # progress bar progress = progress_widget(self, self.appctxt) progress.set_percent() progress.set_activity( "Downloading latest version ({})".format(return_url) ) # setup downloader thread downloader = version.download_new_version_thread(return_url) downloader.activity_update.connect(progress.set_percentage) def failed(err): typ = type(err) message = err logger.exception("Failed to download new version") QtWidgets.QMessageBox().warning( self, "Error", "Something went terribly wrong.\n{}: {}".format( typ, message ), ) # start the thread with thread.thread_wait( downloader.finished, finish_func=version.install_new_version, failed_signal=downloader.failed, failed_func=failed, update_signal=downloader.activity_update, ): downloader.start() progress.close() else: webbrowser.open(return_url) elif remember: config.set_key_value(config.NEVER_VER_CHEK_KEY, True)
def set_theme(appctxt: ApplicationContext, fs_theme: bool) -> None: """Writes theme selection to config file and sets the app stylesheet.""" logger.debug("Writing theme selection to config file") if fs_theme: config.set_key_value(config.THEME_KEY, FS_THEME) # apply stylesheet logger.debug("Applying application stylesheet {}".format( appctxt.get_resource("fs_style.qss"))) stylesheet = appctxt.get_resource("fs_style.qss") appctxt.app.setStyleSheet(open(stylesheet, "r").read()) else: config.set_key_value(config.THEME_KEY, "None") logger.debug("Clearing application stylesheet") appctxt.app.setStyleSheet("")
def get_mod_install_folder() -> str: """Gets the current mod install folder value from the config file.""" succeeded, value = config.get_key_value(config.MOD_INSTALL_FOLDER_KEY, path=True) if not succeeded: # if mod install folder could not be loaded from config value = os.path.abspath(os.path.join(config.BASE_FOLDER, "modCache")) config.set_key_value(config.MOD_INSTALL_FOLDER_KEY, value, path=True) mod_install_folder = fix_path(value) if not os.path.exists(mod_install_folder): logger.debug( "Creating mod install folder {}".format(mod_install_folder)) os.makedirs(mod_install_folder) return mod_install_folder
def check_version(self) -> None: """Checks the application version and allows user to open browser to update.""" installed = version.is_installed() return_url = version.check_version(self.appctxt, installed) # type: ignore def core(progress: Callable) -> None: progress.set_mode(progress.PERCENT) progress.set_activity( "Downloading latest version ({})".format(return_url)) # setup downloader thread downloader = version.download_new_version_thread( return_url) # type: ignore downloader.percent_update.connect( progress.set_percent) # type: ignore def failed(err: Exception) -> None: typ = type(err) message = str(err) logger.exception("Failed to download new version") error_dialogs.general(self, typ, message) # start the thread with thread.thread_wait( downloader.finished, finish_func=version.install_new_version, failed_signal=downloader.failed, failed_func=failed, update_signal=downloader.percent_update, ): downloader.start() if not return_url: return result, remember = version_check_dialog(self, installed).exec_() if result: if installed: self.base_action(core, refresh=False) else: webbrowser.open(return_url) # type: ignore elif remember: config.set_key_value(config.NEVER_VER_CHEK_KEY, True)
def user_selection(): """Function to keep user in a loop until they select correct folder.""" # prompt user to select self.flight_sim.sim_packages_folder = ( QtWidgets.QFileDialog.getExistingDirectory( parent=self, caption= "Select the root Microsoft Flight Simulator directory", dir=os.getenv("APPDATA"), )) if not self.flight_sim.sim_packages_folder.strip(): sys.exit() elif self.flight_sim.is_sim_packages_folder( self.flight_sim.sim_packages_folder): # save the config file config.set_key_value( config.SIM_FOLDER_KEY, self.flight_sim.sim_packages_folder, path=True, ) elif self.flight_sim.is_sim_packages_folder( os.path.join(self.flight_sim.sim_packages_folder, "Packages")): # save the config file config.set_key_value( config.SIM_FOLDER_KEY, os.path.join(self.flight_sim.sim_packages_folder, "Packages"), path=True, ) else: # show error warning_dialogs.sim_path_invalid(self) # send them through again user_selection()
def move_mod_install_folder( self, src: str, dest: str, update_func: Callable = None ) -> None: """Moves the mod install folder.""" logger.debug("Moving mod install folder from {} to {}".format(src, dest)) # first, build a list of the currently enabled mods enabled_mod_folders = files.listdir_dirs(self.get_sim_mod_folder()) # move the install folder files.move_folder(src, dest, update_func=update_func) # set new config value config.set_key_value(config.MOD_INSTALL_FOLDER_KEY, dest, path=True) # clear the cache self.clear_mod_cache() config.get_key_value.cache_clear() # now, go through mods in the install folder and re-enable them # if they were enabled before. moved_mod_folders = files.listdir_dirs(dest) for mod_folder in moved_mod_folders: if mod_folder in enabled_mod_folders: self.enable_mod(mod_folder, update_func=update_func)
def install_folder(self): """Installs selected mod folders.""" # first, let user select a folder mod_folder = QtWidgets.QFileDialog.getExistingDirectory( parent=self, caption="Select mod folder", dir=files.get_last_open_folder(), ) succeeded = [] def core(progress): def finish(result): # this function is required as the results will be a list, # which is not a hashable type succeeded.extend(result) def failed(error): mapping = { flight_sim.NoManifestError: lambda: warning_dialogs.mod_parsing(self, [mod_folder]), files.AccessError: lambda: error_dialogs.permission(self, mod_folder, error), flight_sim.NoModsError: lambda: error_dialogs.no_mods(self, mod_folder), } self.base_fail( error, mapping, "Failed to install mod folder", ) # setup installer thread installer = flight_sim.install_mods_thread(self.flight_sim, mod_folder) installer.activity_update.connect(progress.set_activity) # start the thread with thread.thread_wait( installer.finished, finish_func=finish, failed_signal=installer.failed, failed_func=failed, update_signal=installer.activity_update, ): installer.start() self.base_action( core, button=self.install_button, empty_check=True, empty_val=mod_folder, ) if succeeded: config.set_key_value(config.LAST_OPEN_FOLDER_KEY, os.path.dirname(mod_folder), path=True) information_dialogs.mods_installed(self, succeeded)
def install_archive(self): """Installs selected mod archives.""" # first, let user select multiple archives mod_archives = QtWidgets.QFileDialog.getOpenFileNames( parent=self, caption="Select mod archive(s)", dir=files.get_last_open_folder(), filter=ARCHIVE_FILTER, )[0] succeeded = [] def core(progress): # for each archive, try to install it for mod_archive in mod_archives: def finish(result): # this function is required as the results will be a list, # which is not a hashable type succeeded.extend(result) def failed(error): mapping = { files.ExtractionError: lambda: error_dialogs.archive_extract( self, mod_archive, error), flight_sim.NoManifestError: lambda: warning_dialogs.mod_parsing( self, [mod_archive]), files.AccessError: lambda: error_dialogs.permission( self, mod_archive, error), flight_sim.NoModsError: lambda: error_dialogs.no_mods(self, mod_archive), } self.base_fail( error, mapping, "Failed to install mod archive", ) # setup installer thread installer = flight_sim.install_mod_archive_thread( self.flight_sim, mod_archive) installer.activity_update.connect(progress.set_activity) installer.percent_update.connect(progress.set_percent) # start the thread with thread.thread_wait( installer.finished, finish_func=finish, failed_signal=installer.failed, failed_func=failed, update_signal=installer.activity_update, timeout=1200000, ): installer.start() self.base_action( core, button=self.install_button, empty_check=True, empty_val=mod_archives, ) if succeeded: config.set_key_value(config.LAST_OPEN_FOLDER_KEY, os.path.dirname(mod_archives[0]), path=True) information_dialogs.mods_installed(self, succeeded)
def check_version(appctxt, installed=False): """Returns the release URL if a new version is installed. Otherwise, returns False.""" logger.debug("Checking if a new version is available") time_format = "%Y-%m-%d %H:%M:%S" if not check_version_config(time_format): return False # open the remote url url = "https://api.github.com/repos/NathanVaughn/msfs-mod-manager/releases/latest" try: logger.debug("Attempting to open url {}".format(url)) # always will be opening the above hard-coded URL page = urllib.request.urlopen(url) # nosec except Exception: logger.exception("Opening url {} failed".format(url)) return False # read page contents logger.debug("Reading page contents") data = page.read() data = data.decode("utf-8") # parse the json try: logger.debug("Attemping to parse page contents") parsed_data = json.loads(data) remote_version = parsed_data["tag_name"] except Exception: logger.exception("Parsing page contents failed") return False logger.debug("Remote version found is: {}".format(remote_version)) # write the config file back out logger.debug("Writing out last version check time to config file") config.set_key_value( config.LAST_VER_CHECK_KEY, datetime.datetime.strftime(datetime.datetime.now(), time_format), ) # check if remote version is newer than local version if remote_version > get_version(appctxt): # if so, return release url logger.debug("Remote version is newer than local version") download_url = False if installed: # return setup.exe url for asset in parsed_data["assets"]: if asset["name"].endswith(".exe"): download_url = asset["browser_download_url"] break else: # return release url download_url = parsed_data["html_url"] logger.debug("New release url: {}".format(download_url)) return download_url else: logger.debug("Remote version is not newer than local version") return False
def find_sim(self) -> None: """Sets the path to the simulator root folder.""" def user_selection() -> None: """Function to keep user in a loop until they select correct folder.""" # prompt user to select self.flight_sim.sim_packages_folder = ( QtWidgets.QFileDialog.getExistingDirectory( parent=self, caption= "Select the root Microsoft Flight Simulator directory", dir=os.getenv("APPDATA"), # type: ignore )) if not self.flight_sim.sim_packages_folder.strip(): sys.exit() elif self.flight_sim.is_sim_packages_folder( self.flight_sim.sim_packages_folder): # save the config file config.set_key_value( config.SIM_FOLDER_KEY, self.flight_sim.sim_packages_folder, path=True, ) elif self.flight_sim.is_sim_packages_folder( os.path.join(self.flight_sim.sim_packages_folder, "Packages")): # save the config file config.set_key_value( config.SIM_FOLDER_KEY, os.path.join(self.flight_sim.sim_packages_folder, "Packages"), path=True, ) else: # show error warning_dialogs.sim_path_invalid(self) # send them through again user_selection() # try to automatically find the sim ( success, self.flight_sim.sim_packages_folder, # type: ignore ) = self.flight_sim.find_sim_packages_folder() if not self.flight_sim.sim_packages_folder: # show error warning_dialogs.sim_not_detected(self) # let user select folder user_selection() elif not success: # save the config file config.set_key_value(config.SIM_FOLDER_KEY, self.flight_sim.sim_packages_folder, path=True) # notify user information_dialogs.sim_detected( self, self.flight_sim.sim_packages_folder)