mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-09 08:44:15 +02:00
[improvement][ui] uninstallation does not refresh the table if it succeeds
This commit is contained in:
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- AppImage
|
- AppImage
|
||||||
- able to update AppImages with continuous releases
|
- able to update AppImages with continuous releases
|
||||||
- UI
|
- UI
|
||||||
- not performing a full table refresh after installing a new package
|
- not performing a full table refresh after installing and uninstalling packages
|
||||||
- filters algorithm speed and sorting
|
- filters algorithm speed and sorting
|
||||||
- displaying wait cursor over some components
|
- displaying wait cursor over some components
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ class TransactionResult:
|
|||||||
self.installed = installed
|
self.installed = installed
|
||||||
self.removed = removed
|
self.removed = removed
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fail() -> "TransactionResult":
|
||||||
|
return TransactionResult(success=False, installed=None, removed=None)
|
||||||
|
|
||||||
|
|
||||||
class SoftwareManager(ABC):
|
class SoftwareManager(ABC):
|
||||||
|
|
||||||
@@ -146,12 +150,13 @@ class SoftwareManager(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def uninstall(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||||
"""
|
"""
|
||||||
:param pkg:
|
:param pkg:
|
||||||
:param root_password: the root user password (if required)
|
:param root_password: the root user password (if required)
|
||||||
:param watcher:
|
:param watcher:
|
||||||
:return: if the uninstall succeeded
|
:param disk_loader:
|
||||||
|
:return:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ class AppImageManager(SoftwareManager):
|
|||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if self.uninstall(pkg, root_password, watcher):
|
if self.uninstall(pkg, root_password, watcher).success:
|
||||||
old_release = versions.history[versions.pkg_status_idx + 1]
|
old_release = versions.history[versions.pkg_status_idx + 1]
|
||||||
pkg.version = old_release['0_version']
|
pkg.version = old_release['0_version']
|
||||||
pkg.latest_version = pkg.version
|
pkg.latest_version = pkg.version
|
||||||
@@ -300,7 +300,7 @@ class AppImageManager(SoftwareManager):
|
|||||||
for req in requirements.to_upgrade:
|
for req in requirements.to_upgrade:
|
||||||
watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version))
|
watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version))
|
||||||
|
|
||||||
if not self.uninstall(req.pkg, root_password, watcher):
|
if not self.uninstall(req.pkg, root_password, watcher).success:
|
||||||
watcher.show_message(title=self.i18n['error'],
|
watcher.show_message(title=self.i18n['error'],
|
||||||
body=self.i18n['appimage.error.uninstall_current_version'],
|
body=self.i18n['appimage.error.uninstall_current_version'],
|
||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
@@ -316,13 +316,13 @@ class AppImageManager(SoftwareManager):
|
|||||||
watcher.change_substatus('')
|
watcher.change_substatus('')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def uninstall(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader = None) -> TransactionResult:
|
||||||
if os.path.exists(pkg.get_disk_cache_path()):
|
if os.path.exists(pkg.get_disk_cache_path()):
|
||||||
handler = ProcessHandler(watcher)
|
handler = ProcessHandler(watcher)
|
||||||
|
|
||||||
if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))):
|
if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))):
|
||||||
watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path())))
|
watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path())))
|
||||||
return False
|
return TransactionResult.fail()
|
||||||
|
|
||||||
de_path = self._gen_desktop_entry_path(pkg)
|
de_path = self._gen_desktop_entry_path(pkg)
|
||||||
if os.path.exists(de_path):
|
if os.path.exists(de_path):
|
||||||
@@ -330,7 +330,7 @@ class AppImageManager(SoftwareManager):
|
|||||||
|
|
||||||
self.revert_ignored_update(pkg)
|
self.revert_ignored_update(pkg)
|
||||||
|
|
||||||
return True
|
return TransactionResult(success=True, installed=None, removed=[pkg])
|
||||||
|
|
||||||
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
||||||
return {AppImage}
|
return {AppImage}
|
||||||
@@ -508,7 +508,6 @@ class AppImageManager(SoftwareManager):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
pkg.installed = True
|
|
||||||
return TransactionResult(success=True, installed=[pkg], removed=[])
|
return TransactionResult(success=True, installed=[pkg], removed=[])
|
||||||
else:
|
else:
|
||||||
watcher.show_message(title=self.i18n['error'],
|
watcher.show_message(title=self.i18n['error'],
|
||||||
|
|||||||
@@ -997,9 +997,11 @@ class ArchManager(SoftwareManager):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _uninstall(self, context: TransactionContext, remove_unneeded: bool = False):
|
def _uninstall(self, context: TransactionContext, remove_unneeded: bool = False, disk_loader: DiskCacheLoader = None):
|
||||||
self._update_progress(context, 10)
|
self._update_progress(context, 10)
|
||||||
|
|
||||||
|
net_available = internet.is_available() if disk_loader else True
|
||||||
|
|
||||||
required_by = self.deps_analyser.map_all_required_by({context.name}, set())
|
required_by = self.deps_analyser.map_all_required_by({context.name}, set())
|
||||||
|
|
||||||
if required_by:
|
if required_by:
|
||||||
@@ -1040,9 +1042,26 @@ class ArchManager(SoftwareManager):
|
|||||||
else:
|
else:
|
||||||
all_deps_map = None
|
all_deps_map = None
|
||||||
|
|
||||||
|
if disk_loader and len(to_uninstall) > 1: # loading package instances in case the uninstall succeeds
|
||||||
|
instances = self.read_installed(disk_loader=disk_loader,
|
||||||
|
names={n for n in to_uninstall if n != context.name},
|
||||||
|
internet_available=net_available).installed
|
||||||
|
|
||||||
|
if len(instances) + 1 < len(to_uninstall):
|
||||||
|
self.logger.warning("Not all packages to be uninstalled could be read")
|
||||||
|
else:
|
||||||
|
instances = None
|
||||||
|
|
||||||
uninstalled = self._uninstall_pkgs(to_uninstall, context.root_password, context.handler)
|
uninstalled = self._uninstall_pkgs(to_uninstall, context.root_password, context.handler)
|
||||||
|
|
||||||
if uninstalled:
|
if uninstalled:
|
||||||
|
if disk_loader: # loading package instances in case the uninstall succeeds
|
||||||
|
context.removed[context.pkg.name] = context.pkg
|
||||||
|
|
||||||
|
if instances:
|
||||||
|
for p in instances:
|
||||||
|
context.removed[p.name] = p
|
||||||
|
|
||||||
self._update_progress(context, 70)
|
self._update_progress(context, 70)
|
||||||
|
|
||||||
if all_deps_map:
|
if all_deps_map:
|
||||||
@@ -1076,10 +1095,22 @@ class ArchManager(SoftwareManager):
|
|||||||
all_unnecessary_to_uninstall = {*unnecessary_to_uninstall, *unnecessary_to_uninstall_deps}
|
all_unnecessary_to_uninstall = {*unnecessary_to_uninstall, *unnecessary_to_uninstall_deps}
|
||||||
|
|
||||||
if not unnecessary_to_uninstall_deps or self._request_all_unncessary_uninstall_confirmation(all_unnecessary_to_uninstall, context):
|
if not unnecessary_to_uninstall_deps or self._request_all_unncessary_uninstall_confirmation(all_unnecessary_to_uninstall, context):
|
||||||
|
|
||||||
|
if disk_loader: # loading package instances in case the uninstall succeeds
|
||||||
|
unnecessary_instances = self.read_installed(disk_loader=disk_loader,
|
||||||
|
internet_available=net_available,
|
||||||
|
names=all_unnecessary_to_uninstall).installed
|
||||||
|
else:
|
||||||
|
unnecessary_instances = None
|
||||||
|
|
||||||
unneded_uninstalled = self._uninstall_pkgs(all_unnecessary_to_uninstall, context.root_password, context.handler)
|
unneded_uninstalled = self._uninstall_pkgs(all_unnecessary_to_uninstall, context.root_password, context.handler)
|
||||||
|
|
||||||
if unneded_uninstalled:
|
if unneded_uninstalled:
|
||||||
to_uninstall.update(all_unnecessary_to_uninstall)
|
to_uninstall.update(all_unnecessary_to_uninstall)
|
||||||
|
|
||||||
|
if disk_loader and unnecessary_instances: # loading package instances in case the uninstall succeeds
|
||||||
|
for p in unnecessary_instances:
|
||||||
|
context.removed[p.name] = p
|
||||||
else:
|
else:
|
||||||
self.logger.error("Could not uninstall some unnecessary packages")
|
self.logger.error("Could not uninstall some unnecessary packages")
|
||||||
context.watcher.print("Could not uninstall some unnecessary packages")
|
context.watcher.print("Could not uninstall some unnecessary packages")
|
||||||
@@ -1102,20 +1133,28 @@ class ArchManager(SoftwareManager):
|
|||||||
self._update_progress(context, 100)
|
self._update_progress(context, 100)
|
||||||
return uninstalled
|
return uninstalled
|
||||||
|
|
||||||
def uninstall(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||||
self.aur_client.clean_caches()
|
self.aur_client.clean_caches()
|
||||||
handler = ProcessHandler(watcher)
|
handler = ProcessHandler(watcher)
|
||||||
|
|
||||||
if self._is_database_locked(handler, root_password):
|
if self._is_database_locked(handler, root_password):
|
||||||
return False
|
return TransactionResult.fail()
|
||||||
|
|
||||||
return self._uninstall(TransactionContext(name=pkg.name,
|
removed = {}
|
||||||
change_progress=True,
|
success = self._uninstall(TransactionContext(name=pkg.name,
|
||||||
arch_config=read_config(),
|
pkg=pkg,
|
||||||
watcher=watcher,
|
change_progress=True,
|
||||||
root_password=root_password,
|
arch_config=read_config(),
|
||||||
handler=handler),
|
watcher=watcher,
|
||||||
remove_unneeded=True)
|
root_password=root_password,
|
||||||
|
handler=handler,
|
||||||
|
removed=removed),
|
||||||
|
remove_unneeded=True,
|
||||||
|
disk_loader=disk_loader) # to be able to return all uninstalled packages
|
||||||
|
if success:
|
||||||
|
return TransactionResult(success=True, installed=None, removed=[*removed.values()] if removed else [])
|
||||||
|
else:
|
||||||
|
return TransactionResult.fail()
|
||||||
|
|
||||||
def get_managed_types(self) -> Set["type"]:
|
def get_managed_types(self) -> Set["type"]:
|
||||||
return {ArchPackage}
|
return {ArchPackage}
|
||||||
@@ -1744,9 +1783,6 @@ class ArchManager(SoftwareManager):
|
|||||||
else:
|
else:
|
||||||
uninstalled = [p for p in pkgs_to_uninstall if p.name == conflict]
|
uninstalled = [p for p in pkgs_to_uninstall if p.name == conflict]
|
||||||
if uninstalled:
|
if uninstalled:
|
||||||
uninstalled[0].icon_path = None
|
|
||||||
uninstalled[0].icon_url = None
|
|
||||||
uninstalled[0].installed = False
|
|
||||||
context.removed[conflict] = uninstalled[0]
|
context.removed[conflict] = uninstalled[0]
|
||||||
else:
|
else:
|
||||||
self.logger.info("No conflict detected for '{}'".format(context.name))
|
self.logger.info("No conflict detected for '{}'".format(context.name))
|
||||||
@@ -1799,9 +1835,6 @@ class ArchManager(SoftwareManager):
|
|||||||
if installed_with_same_name:
|
if installed_with_same_name:
|
||||||
for p in installed_with_same_name:
|
for p in installed_with_same_name:
|
||||||
context.removed[p.name] = p
|
context.removed[p.name] = p
|
||||||
p.installed = False
|
|
||||||
p.icon_path = None
|
|
||||||
p.icon_url = None
|
|
||||||
|
|
||||||
context.watcher.change_substatus(self.i18n['status.caching_data'].format(bold(context.name)))
|
context.watcher.change_substatus(self.i18n['status.caching_data'].format(bold(context.name)))
|
||||||
|
|
||||||
|
|||||||
@@ -226,15 +226,17 @@ class FlatpakManager(SoftwareManager):
|
|||||||
watcher.change_substatus('')
|
watcher.change_substatus('')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref, pkg.installation)))
|
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref, pkg.installation)))
|
||||||
|
|
||||||
if self.suggestions_cache:
|
if uninstalled:
|
||||||
self.suggestions_cache.delete(pkg.id)
|
if self.suggestions_cache:
|
||||||
|
self.suggestions_cache.delete(pkg.id)
|
||||||
|
|
||||||
self.revert_ignored_update(pkg)
|
self.revert_ignored_update(pkg)
|
||||||
|
return TransactionResult(success=True, installed=None, removed=[pkg])
|
||||||
|
|
||||||
return uninstalled
|
return TransactionResult.fail()
|
||||||
|
|
||||||
def get_info(self, app: FlatpakApplication) -> dict:
|
def get_info(self, app: FlatpakApplication) -> dict:
|
||||||
if app.installed:
|
if app.installed:
|
||||||
@@ -354,7 +356,6 @@ class FlatpakManager(SoftwareManager):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
pkg.installed = res
|
|
||||||
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
|
|||||||
@@ -142,13 +142,16 @@ class SnapManager(SoftwareManager):
|
|||||||
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> SystemProcess:
|
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> SystemProcess:
|
||||||
raise Exception("'upgrade' is not supported by {}".format(SnapManager.__class__.__name__))
|
raise Exception("'upgrade' is not supported by {}".format(SnapManager.__class__.__name__))
|
||||||
|
|
||||||
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=snap.uninstall_and_stream(pkg.name, root_password)))
|
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=snap.uninstall_and_stream(pkg.name, root_password)))
|
||||||
|
|
||||||
if self.suggestions_cache:
|
if uninstalled:
|
||||||
self.suggestions_cache.delete(pkg.name)
|
if self.suggestions_cache:
|
||||||
|
self.suggestions_cache.delete(pkg.name)
|
||||||
|
|
||||||
return uninstalled
|
return TransactionResult(success=True, installed=None, removed=[pkg])
|
||||||
|
|
||||||
|
return TransactionResult.fail()
|
||||||
|
|
||||||
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
||||||
return {SnapApplication}
|
return {SnapApplication}
|
||||||
@@ -205,7 +208,6 @@ class SnapManager(SoftwareManager):
|
|||||||
if res and info_path:
|
if res and info_path:
|
||||||
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
|
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
|
||||||
|
|
||||||
pkg.installed = res
|
|
||||||
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
||||||
else:
|
else:
|
||||||
self.logger.error("Could not find available channels in the installation output: {}".format(output))
|
self.logger.error("Could not find available channels in the installation output: {}".format(output))
|
||||||
@@ -213,7 +215,6 @@ class SnapManager(SoftwareManager):
|
|||||||
if info_path:
|
if info_path:
|
||||||
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
|
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
|
||||||
|
|
||||||
pkg.installed = res
|
|
||||||
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
|
||||||
|
|
||||||
def is_enabled(self) -> bool:
|
def is_enabled(self) -> bool:
|
||||||
|
|||||||
@@ -348,14 +348,14 @@ class WebApplicationManager(SoftwareManager):
|
|||||||
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> bool:
|
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def uninstall(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
def uninstall(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||||
self.logger.info("Checking if {} installation directory {} exists".format(pkg.name, pkg.installation_dir))
|
self.logger.info("Checking if {} installation directory {} exists".format(pkg.name, pkg.installation_dir))
|
||||||
|
|
||||||
if not os.path.exists(pkg.installation_dir):
|
if not os.path.exists(pkg.installation_dir):
|
||||||
watcher.show_message(title=self.i18n['error'],
|
watcher.show_message(title=self.i18n['error'],
|
||||||
body=self.i18n['web.uninstall.error.install_dir.not_found'].format(bold(pkg.installation_dir)),
|
body=self.i18n['web.uninstall.error.install_dir.not_found'].format(bold(pkg.installation_dir)),
|
||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
return False
|
return TransactionResult.fail()
|
||||||
|
|
||||||
self.logger.info("Removing {} installation directory {}".format(pkg.name, pkg.installation_dir))
|
self.logger.info("Removing {} installation directory {}".format(pkg.name, pkg.installation_dir))
|
||||||
try:
|
try:
|
||||||
@@ -365,7 +365,7 @@ class WebApplicationManager(SoftwareManager):
|
|||||||
body=self.i18n['web.uninstall.error.remove'].format(bold(pkg.installation_dir)),
|
body=self.i18n['web.uninstall.error.remove'].format(bold(pkg.installation_dir)),
|
||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return TransactionResult.fail()
|
||||||
|
|
||||||
self.logger.info("Checking if {} desktop entry file {} exists".format(pkg.name, pkg.desktop_entry))
|
self.logger.info("Checking if {} desktop entry file {} exists".format(pkg.name, pkg.desktop_entry))
|
||||||
if os.path.exists(pkg.desktop_entry):
|
if os.path.exists(pkg.desktop_entry):
|
||||||
@@ -412,7 +412,8 @@ class WebApplicationManager(SoftwareManager):
|
|||||||
watcher.show_message(title=self.i18n['error'],
|
watcher.show_message(title=self.i18n['error'],
|
||||||
body=self.i18n['web.uninstall.error.remove'].format(bold(fix_path)),
|
body=self.i18n['web.uninstall.error.remove'].format(bold(fix_path)),
|
||||||
type_=MessageType.WARNING)
|
type_=MessageType.WARNING)
|
||||||
return True
|
|
||||||
|
return TransactionResult(success=True, installed=None, removed=[pkg])
|
||||||
|
|
||||||
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
||||||
return {WebApplication}
|
return {WebApplication}
|
||||||
@@ -763,7 +764,6 @@ class WebApplicationManager(SoftwareManager):
|
|||||||
if install_options:
|
if install_options:
|
||||||
pkg.options_set = install_options
|
pkg.options_set = install_options
|
||||||
|
|
||||||
pkg.installed = True
|
|
||||||
return TransactionResult(success=True, installed=[pkg], removed=[])
|
return TransactionResult(success=True, installed=[pkg], removed=[])
|
||||||
|
|
||||||
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:
|
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:
|
||||||
|
|||||||
@@ -285,20 +285,43 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def uninstall(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
|
|
||||||
man = self._get_manager_for(app)
|
|
||||||
|
|
||||||
if man:
|
|
||||||
return man.uninstall(app, root_password, handler)
|
|
||||||
|
|
||||||
def _fill_post_transaction_status(self, pkg: SoftwarePackage, installed: bool):
|
def _fill_post_transaction_status(self, pkg: SoftwarePackage, installed: bool):
|
||||||
pkg.installed = installed
|
pkg.installed = installed
|
||||||
|
|
||||||
pkg.update = False
|
pkg.update = False
|
||||||
|
|
||||||
if pkg.latest_version:
|
if pkg.latest_version:
|
||||||
pkg.version = pkg.latest_version
|
pkg.version = pkg.latest_version
|
||||||
|
|
||||||
|
def _update_post_transaction_status(self, res: TransactionResult):
|
||||||
|
if res.success:
|
||||||
|
if res.installed:
|
||||||
|
for p in res.installed:
|
||||||
|
self._fill_post_transaction_status(p, True)
|
||||||
|
if res.removed:
|
||||||
|
for p in res.removed:
|
||||||
|
self._fill_post_transaction_status(p, False)
|
||||||
|
|
||||||
|
def uninstall(self, pkg: SoftwarePackage, root_password: str, handler: ProcessWatcher, disk_loader: DiskCacheLoader = None) -> TransactionResult:
|
||||||
|
man = self._get_manager_for(pkg)
|
||||||
|
|
||||||
|
if man:
|
||||||
|
ti = time.time()
|
||||||
|
disk_loader = self.disk_loader_factory.new()
|
||||||
|
disk_loader.start()
|
||||||
|
self.logger.info("Uninstalling {}".format(pkg.name))
|
||||||
|
try:
|
||||||
|
res = man.uninstall(pkg, root_password, handler, disk_loader)
|
||||||
|
disk_loader.stop_working()
|
||||||
|
disk_loader.join()
|
||||||
|
self._update_post_transaction_status(res)
|
||||||
|
return res
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
return TransactionResult(success=False, installed=[], removed=[])
|
||||||
|
finally:
|
||||||
|
tf = time.time()
|
||||||
|
self.logger.info('Uninstallation of {}'.format(pkg) + 'took {0:.2f} minutes'.format((tf - ti) / 60))
|
||||||
|
|
||||||
def install(self, app: SoftwarePackage, root_password: str, disk_loader: DiskCacheLoader, handler: ProcessWatcher) -> TransactionResult:
|
def install(self, app: SoftwarePackage, root_password: str, disk_loader: DiskCacheLoader, handler: ProcessWatcher) -> TransactionResult:
|
||||||
man = self._get_manager_for(app)
|
man = self._get_manager_for(app)
|
||||||
|
|
||||||
@@ -311,15 +334,7 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
res = man.install(app, root_password, disk_loader, handler)
|
res = man.install(app, root_password, disk_loader, handler)
|
||||||
disk_loader.stop_working()
|
disk_loader.stop_working()
|
||||||
disk_loader.join()
|
disk_loader.join()
|
||||||
|
self._update_post_transaction_status(res)
|
||||||
if res.success:
|
|
||||||
if res.installed:
|
|
||||||
for p in res.installed:
|
|
||||||
self._fill_post_transaction_status(p, True)
|
|
||||||
if res.removed:
|
|
||||||
for p in res.removed:
|
|
||||||
self._fill_post_transaction_status(p, False)
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|||||||
@@ -194,11 +194,11 @@ class AppsTable(QTableWidget):
|
|||||||
|
|
||||||
self._update_row(pkg, change_update_col=change_update_col)
|
self._update_row(pkg, change_update_col=change_update_col)
|
||||||
|
|
||||||
def _uninstall_app(self, app_v: PackageView):
|
def _uninstall(self, pkg: PackageView):
|
||||||
if dialog.ask_confirmation(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
if dialog.ask_confirmation(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
|
||||||
body=self._parag(self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(self._bold(str(app_v)))),
|
body=self._parag(self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(self._bold(str(pkg)))),
|
||||||
i18n=self.i18n):
|
i18n=self.i18n):
|
||||||
self.window.uninstall_app(app_v)
|
self.window.uninstall_package(pkg)
|
||||||
|
|
||||||
def _bold(self, text: str) -> str:
|
def _bold(self, text: str) -> str:
|
||||||
return '<span style="font-weight: bold">{}</span>'.format(text)
|
return '<span style="font-weight: bold">{}</span>'.format(text)
|
||||||
@@ -284,7 +284,7 @@ class AppsTable(QTableWidget):
|
|||||||
if change_update_col:
|
if change_update_col:
|
||||||
col_update = None
|
col_update = None
|
||||||
|
|
||||||
if update_check_enabled and not pkg.model.is_update_ignored() and pkg.model.update:
|
if update_check_enabled and pkg.model.installed and not pkg.model.is_update_ignored() and pkg.model.update:
|
||||||
col_update = QToolBar()
|
col_update = QToolBar()
|
||||||
col_update.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
col_update.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||||
col_update.addWidget(UpdateToggleButton(pkg=pkg,
|
col_update.addWidget(UpdateToggleButton(pkg=pkg,
|
||||||
@@ -323,7 +323,7 @@ class AppsTable(QTableWidget):
|
|||||||
if pkg.model.installed:
|
if pkg.model.installed:
|
||||||
if pkg.model.can_be_uninstalled():
|
if pkg.model.can_be_uninstalled():
|
||||||
def uninstall():
|
def uninstall():
|
||||||
self._uninstall_app(pkg)
|
self._uninstall(pkg)
|
||||||
|
|
||||||
style = 'color: {c}; font-size: 10px; font-weight: bold;'.format(c=BROWN)
|
style = 'color: {c}; font-size: 10px; font-weight: bold;'.format(c=BROWN)
|
||||||
item = self._gen_row_button(self.i18n['uninstall'].capitalize(), style, uninstall)
|
item = self._gen_row_button(self.i18n['uninstall'].capitalize(), style, uninstall)
|
||||||
@@ -408,7 +408,7 @@ class AppsTable(QTableWidget):
|
|||||||
item.setText(name)
|
item.setText(name)
|
||||||
|
|
||||||
icon_path = pkg.model.get_disk_icon_path()
|
icon_path = pkg.model.get_disk_icon_path()
|
||||||
if pkg.model.supports_disk_cache() and icon_path:
|
if pkg.model.installed and pkg.model.supports_disk_cache() and icon_path:
|
||||||
if icon_path.startswith('/') and os.path.isfile(icon_path):
|
if icon_path.startswith('/') and os.path.isfile(icon_path):
|
||||||
with open(icon_path, 'rb') as f:
|
with open(icon_path, 'rb') as f:
|
||||||
icon_bytes = f.read()
|
icon_bytes = f.read()
|
||||||
|
|||||||
@@ -496,34 +496,42 @@ class RefreshApps(AsyncAction):
|
|||||||
self.pkg_types = None
|
self.pkg_types = None
|
||||||
|
|
||||||
|
|
||||||
class UninstallApp(AsyncAction):
|
class UninstallPackage(AsyncAction):
|
||||||
|
|
||||||
def __init__(self, manager: SoftwareManager, icon_cache: MemoryCache, i18n: I18n, app: PackageView = None):
|
def __init__(self, manager: SoftwareManager, icon_cache: MemoryCache, i18n: I18n, pkg: PackageView = None):
|
||||||
super(UninstallApp, self).__init__()
|
super(UninstallPackage, self).__init__()
|
||||||
self.app = app
|
self.pkg = pkg
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.icon_cache = icon_cache
|
self.icon_cache = icon_cache
|
||||||
self.root_pwd = None
|
self.root_pwd = None
|
||||||
self.i18n = i18n
|
self.i18n = i18n
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.app:
|
if self.pkg:
|
||||||
if self.app.model.supports_backup():
|
if self.pkg.model.supports_backup():
|
||||||
if not self.request_backup(read_config(), 'uninstall', self.i18n, self.root_pwd):
|
if not self.request_backup(read_config(), 'uninstall', self.i18n, self.root_pwd):
|
||||||
self.notify_finished(False)
|
self.notify_finished(False)
|
||||||
self.app = None
|
self.pkg = None
|
||||||
self.root_pwd = None
|
self.root_pwd = None
|
||||||
return
|
return
|
||||||
|
|
||||||
success = self.manager.uninstall(self.app.model, self.root_pwd, self)
|
try:
|
||||||
|
res = self.manager.uninstall(self.pkg.model, self.root_pwd, self, None)
|
||||||
|
|
||||||
if success:
|
if res.success and res.removed:
|
||||||
self.icon_cache.delete(self.app.model.icon_url)
|
for p in res.removed:
|
||||||
self.manager.clean_cache_for(self.app.model)
|
if p.icon_url:
|
||||||
|
self.icon_cache.delete(p.icon_url)
|
||||||
|
|
||||||
self.notify_finished(self.app if success else None)
|
self.manager.clean_cache_for(p)
|
||||||
self.app = None
|
|
||||||
self.root_pwd = None
|
self.notify_finished({'success': res.success, 'removed': res.removed, 'pkg': self.pkg})
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
self.notify_finished({'success': False, 'removed': None, 'pkg': self.pkg})
|
||||||
|
finally:
|
||||||
|
self.pkg = None
|
||||||
|
self.root_pwd = None
|
||||||
|
|
||||||
|
|
||||||
class DowngradeApp(AsyncAction):
|
class DowngradeApp(AsyncAction):
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from bauh.view.qt.info import InfoDialog
|
|||||||
from bauh.view.qt.root import ask_root_password
|
from bauh.view.qt.root import ask_root_password
|
||||||
from bauh.view.qt.screenshots import ScreenshotsDialog
|
from bauh.view.qt.screenshots import ScreenshotsDialog
|
||||||
from bauh.view.qt.settings import SettingsWindow
|
from bauh.view.qt.settings import SettingsWindow
|
||||||
from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
|
from bauh.view.qt.thread import UpgradeSelected, RefreshApps, UninstallPackage, DowngradeApp, GetAppInfo, \
|
||||||
GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, NotifyPackagesReady, FindSuggestions, \
|
GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, NotifyPackagesReady, FindSuggestions, \
|
||||||
ListWarnings, \
|
ListWarnings, \
|
||||||
AsyncAction, LaunchApp, ApplyFilters, CustomSoftwareAction, GetScreenshots, CustomAction, NotifyInstalledLoaded, \
|
AsyncAction, LaunchApp, ApplyFilters, CustomSoftwareAction, GetScreenshots, CustomAction, NotifyInstalledLoaded, \
|
||||||
@@ -303,7 +303,7 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
self.thread_update = self._bind_async_action(UpgradeSelected(self.manager, self.i18n), finished_call=self._finish_upgrade_selected)
|
self.thread_update = self._bind_async_action(UpgradeSelected(self.manager, self.i18n), finished_call=self._finish_upgrade_selected)
|
||||||
self.thread_refresh = self._bind_async_action(RefreshApps(self.manager), finished_call=self._finish_refresh_apps, only_finished=True)
|
self.thread_refresh = self._bind_async_action(RefreshApps(self.manager), finished_call=self._finish_refresh_apps, only_finished=True)
|
||||||
self.thread_uninstall = self._bind_async_action(UninstallApp(self.manager, self.icon_cache, self.i18n), finished_call=self._finish_uninstall)
|
self.thread_uninstall = self._bind_async_action(UninstallPackage(self.manager, self.icon_cache, self.i18n), finished_call=self._finish_uninstall)
|
||||||
self.thread_get_info = self._bind_async_action(GetAppInfo(self.manager), finished_call=self._finish_get_info)
|
self.thread_get_info = self._bind_async_action(GetAppInfo(self.manager), finished_call=self._finish_get_info)
|
||||||
self.thread_get_history = self._bind_async_action(GetAppHistory(self.manager, self.i18n), finished_call=self._finish_get_history)
|
self.thread_get_history = self._bind_async_action(GetAppHistory(self.manager, self.i18n), finished_call=self._finish_get_history)
|
||||||
self.thread_search = self._bind_async_action(SearchPackages(self.manager), finished_call=self._finish_search, only_finished=True)
|
self.thread_search = self._bind_async_action(SearchPackages(self.manager), finished_call=self._finish_search, only_finished=True)
|
||||||
@@ -520,7 +520,7 @@ class ManageWindow(QWidget):
|
|||||||
self.ref_checkbox_only_apps.setVisible(True)
|
self.ref_checkbox_only_apps.setVisible(True)
|
||||||
self.input_search.setText('')
|
self.input_search.setText('')
|
||||||
self.input_name_filter.setText('')
|
self.input_name_filter.setText('')
|
||||||
self._begin_action(self.i18n['manage_window.status.installed'], keep_bt_installed=False, clear_filters=not self.recent_uninstall)
|
self._begin_action(self.i18n['manage_window.status.installed'], keep_bt_installed=False)
|
||||||
self.thread_load_installed.start()
|
self.thread_load_installed.start()
|
||||||
|
|
||||||
def _finish_loading_installed(self):
|
def _finish_loading_installed(self):
|
||||||
@@ -606,7 +606,7 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
self.ref_checkbox_updates.setVisible(False)
|
self.ref_checkbox_updates.setVisible(False)
|
||||||
self.ref_checkbox_only_apps.setVisible(False)
|
self.ref_checkbox_only_apps.setVisible(False)
|
||||||
self._begin_action(self.i18n['manage_window.status.refreshing'], keep_bt_installed=False, clear_filters=not self.recent_uninstall)
|
self._begin_action(self.i18n['manage_window.status.refreshing'], keep_bt_installed=False)
|
||||||
|
|
||||||
self.thread_refresh.pkg_types = pkg_types
|
self.thread_refresh.pkg_types = pkg_types
|
||||||
self.thread_refresh.start()
|
self.thread_refresh.start()
|
||||||
@@ -616,7 +616,7 @@ class ManageWindow(QWidget):
|
|||||||
self._handle_console_option(False)
|
self._handle_console_option(False)
|
||||||
self.ref_checkbox_updates.setVisible(False)
|
self.ref_checkbox_updates.setVisible(False)
|
||||||
self.ref_checkbox_only_apps.setVisible(False)
|
self.ref_checkbox_only_apps.setVisible(False)
|
||||||
self._begin_action(self.i18n['manage_window.status.suggestions'], keep_bt_installed=False, clear_filters=not self.recent_uninstall)
|
self._begin_action(self.i18n['manage_window.status.suggestions'], keep_bt_installed=False)
|
||||||
self.thread_suggestions.filter_installed = True
|
self.thread_suggestions.filter_installed = True
|
||||||
self.thread_suggestions.start()
|
self.thread_suggestions.start()
|
||||||
|
|
||||||
@@ -626,21 +626,20 @@ class ManageWindow(QWidget):
|
|||||||
|
|
||||||
self.ref_checkbox_only_apps.setVisible(bool(res['installed']))
|
self.ref_checkbox_only_apps.setVisible(bool(res['installed']))
|
||||||
self.ref_bt_upgrade.setVisible(True)
|
self.ref_bt_upgrade.setVisible(True)
|
||||||
self.update_pkgs(res['installed'], as_installed=as_installed, types=res['types'], keep_filters=self.recent_uninstall and res['types'])
|
self.update_pkgs(res['installed'], as_installed=as_installed, types=res['types'])
|
||||||
self.load_suggestions = False
|
self.load_suggestions = False
|
||||||
self.recent_uninstall = False
|
|
||||||
self.types_changed = False
|
self.types_changed = False
|
||||||
|
|
||||||
def uninstall_app(self, app: PackageView):
|
def uninstall_package(self, pkg: PackageView):
|
||||||
pwd, proceed = self._ask_root_password('uninstall', app)
|
pwd, proceed = self._ask_root_password('uninstall', pkg)
|
||||||
|
|
||||||
if not proceed:
|
if not proceed:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._handle_console_option(True)
|
self._handle_console_option(True)
|
||||||
self._begin_action('{} {}'.format(self.i18n['manage_window.status.uninstalling'], app.model.name), clear_filters=False)
|
self._begin_action('{} {}'.format(self.i18n['manage_window.status.uninstalling'], pkg.model.name), clear_filters=False)
|
||||||
|
|
||||||
self.thread_uninstall.app = app
|
self.thread_uninstall.pkg = pkg
|
||||||
self.thread_uninstall.root_pwd = pwd
|
self.thread_uninstall.root_pwd = pwd
|
||||||
self.thread_uninstall.start()
|
self.thread_uninstall.start()
|
||||||
|
|
||||||
@@ -649,29 +648,64 @@ class ManageWindow(QWidget):
|
|||||||
self.thread_run_app.app = app
|
self.thread_run_app.app = app
|
||||||
self.thread_run_app.start()
|
self.thread_run_app.start()
|
||||||
|
|
||||||
def _finish_uninstall(self, pkgv: PackageView):
|
def _finish_uninstall(self, res: dict):
|
||||||
self.finish_action()
|
self.finish_action(keep_filters=True)
|
||||||
|
|
||||||
if pkgv:
|
if res['success']:
|
||||||
|
src_pkg = res['pkg']
|
||||||
if self._can_notify_user():
|
if self._can_notify_user():
|
||||||
util.notify_user('{} ({}) {}'.format(pkgv.model.name, pkgv.model.get_type(), self.i18n['uninstalled']))
|
util.notify_user('{} ({}) {}'.format(src_pkg.model.name, src_pkg.model.get_type(), self.i18n['uninstalled']))
|
||||||
|
|
||||||
if not self.search_performed:
|
if res['removed']:
|
||||||
only_pkg_type = len([p for p in self.pkgs if p.model.get_type() == pkgv.model.get_type()]) >= 2
|
for list_idx, pkg_list in enumerate((self.pkgs_available, self.pkgs, self.pkgs_installed)):
|
||||||
else:
|
if pkg_list:
|
||||||
only_pkg_type = False
|
removed_idxs = []
|
||||||
|
for pkgv_idx, pkgv in enumerate(pkg_list):
|
||||||
|
if len(removed_idxs) == len(res['removed']):
|
||||||
|
break
|
||||||
|
|
||||||
|
for model in res['removed']:
|
||||||
|
if pkgv.model == model:
|
||||||
|
if list_idx == 0: # updates the model
|
||||||
|
pkgv.model = model
|
||||||
|
|
||||||
|
if not self.search_performed or list_idx == 2: # always from the installed packages
|
||||||
|
removed_idxs.append(pkgv_idx)
|
||||||
|
|
||||||
|
if self.search_performed and list_idx == 1: # only for displayed
|
||||||
|
self.table_apps.update_package(pkgv)
|
||||||
|
|
||||||
|
break # as the model has been found, stops the loop
|
||||||
|
|
||||||
|
if removed_idxs:
|
||||||
|
# updating the list
|
||||||
|
removed_idxs.sort()
|
||||||
|
for decrement, pkg_idx in enumerate(removed_idxs):
|
||||||
|
del pkg_list[pkg_idx - decrement]
|
||||||
|
|
||||||
|
if list_idx == 1: # updates the rows if the current list reprents the displayed packages:
|
||||||
|
for decrement, idx in enumerate(removed_idxs):
|
||||||
|
self.table_apps.removeRow(idx - decrement)
|
||||||
|
|
||||||
|
for new_idx, pkgv in enumerate(self.pkgs): # updating the package indexes
|
||||||
|
pkgv.table_index = new_idx
|
||||||
|
|
||||||
self.recent_uninstall = True
|
|
||||||
self.refresh_packages(pkg_types={pkgv.model.__class__} if only_pkg_type else None)
|
|
||||||
self.update_custom_actions()
|
self.update_custom_actions()
|
||||||
|
|
||||||
notify_tray()
|
notify_tray()
|
||||||
else:
|
else:
|
||||||
if self._can_notify_user():
|
if self._can_notify_user():
|
||||||
util.notify_user('{}: {}'.format(pkgv.model.name, self.i18n['notification.uninstall.failed']))
|
util.notify_user('{}: {}'.format(res['pkg'].model.name, self.i18n['notification.uninstall.failed']))
|
||||||
|
|
||||||
self.checkbox_console.setChecked(True)
|
self.checkbox_console.setChecked(True)
|
||||||
|
|
||||||
|
for ref in (self.ref_combo_categories, self.ref_input_name_filter):
|
||||||
|
if not ref.isVisible():
|
||||||
|
ref.setVisible(True)
|
||||||
|
|
||||||
|
if self.combo_filter_type.count() > 2 and not self.ref_combo_filter_type.isVisible():
|
||||||
|
self.ref_combo_filter_type.setVisible(True)
|
||||||
|
|
||||||
def _can_notify_user(self):
|
def _can_notify_user(self):
|
||||||
return bool(self.config['system']['notifications']) and (self.isHidden() or self.isMinimized())
|
return bool(self.config['system']['notifications']) and (self.isHidden() or self.isMinimized())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user