[improvement][aur] retrieving and displaying all transitive required dependencies

This commit is contained in:
Vinícius Moreira
2019-12-04 12:47:10 -03:00
parent 2aec03586a
commit e01249f49c
23 changed files with 428 additions and 81 deletions

View File

@@ -6,7 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.4] 2019-12 ## [0.7.4] 2019-12
### Improvements ### Improvements
- Some AUR labels have been changed to not confuse the user - AUR:
- retrieving and displaying all transitive required dependencies ( it can be disabled via the new environment variable **BAUH_ARCH_CHECK_SUBDEPS=0** )
- displaying **makedepends** and **checkdepends** in the info window
- Some AUR labels have been changed to not confuse the user
- Minor UI improvements
### Fixes ### Fixes
- AUR: - AUR:

View File

@@ -126,7 +126,8 @@ will be pre-downloaded faster ( it does **NOT** modify your **pacman** settings
( For more information about these optimizations, have a look at [Makepkg](https://wiki.archlinux.org/index.php/Makepkg) ) ( For more information about these optimizations, have a look at [Makepkg](https://wiki.archlinux.org/index.php/Makepkg) )
- Arch package memory-indexer running every 20 minutes. This memory index is used when AUR Api cannot handle the amount of results found for a given search. It can be disabled via the environment variable **BAUH_ARCH_AUR_INDEX_UPDATER=0**. - Arch package memory-indexer running every 20 minutes. This memory index is used when AUR Api cannot handle the amount of results found for a given search. It can be disabled via the environment variable **BAUH_ARCH_AUR_INDEX_UPDATER=0**.
- If some of your installed packages are not categorized, send an e-mail to **bauh4linux@gmail.com** informing their names and categories in the following format: ```name=category1[,category2,category3,...]``` - If some of your installed packages are not categorized, send an e-mail to **bauh4linux@gmail.com** informing their names and categories in the following format: ```name=category1[,category2,category3,...]```
- Transitive dependencies checking can be disabled through the environment variable **BAUH_ARCH_CHECK_SUBDEPS=0**. The dependency checking process will be
faster, but the application will ask for a new confirmation every time a not installed dependency is detected.
### General settings ### General settings
You can change some application settings via environment variables or arguments (type ```bauh --help``` to get more information). You can change some application settings via environment variables or arguments (type ```bauh --help``` to get more information).

View File

@@ -4,6 +4,8 @@ from typing import Set, List
from bauh.api.http import HttpClient from bauh.api.http import HttpClient
import urllib.parse import urllib.parse
from bauh.gems.arch.exceptions import PackageNotFoundException
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&' URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&'
URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h=' URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h='
URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg=' URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg='
@@ -46,5 +48,18 @@ class AURClient:
return info return info
def get_all_dependencies(self, name: str) -> Set[str]:
deps = set()
info = self.get_src_info(name)
if not info:
raise PackageNotFoundException(name)
for attr in ('makedepends', 'depends', 'checkdepends'):
if info.get(attr):
deps.update(info[attr])
return deps
def _map_names_as_queries(self, names) -> str: def _map_names_as_queries(self, names) -> str:
return '&'.join(['arg[{}]={}'.format(i, urllib.parse.quote(n)) for i, n in enumerate(names)]) return '&'.join(['arg[{}]={}'.format(i, urllib.parse.quote(n)) for i, n in enumerate(names)])

View File

@@ -1,4 +1,4 @@
from typing import Set from typing import Set, List, Tuple
from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MultipleSelectComponent, InputOption from bauh.api.abstract.view import MultipleSelectComponent, InputOption
@@ -25,7 +25,7 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
default_options=None) default_options=None)
install = watcher.request_confirmation(title=i18n['arch.install.optdeps.request.title'], install = watcher.request_confirmation(title=i18n['arch.install.optdeps.request.title'],
body='<p>{}.</p><p>{}.</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']), body='<p>{}.</p><p>{}:</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']),
components=[view_opts], components=[view_opts],
confirmation_label=i18n['install'].capitalize(), confirmation_label=i18n['install'].capitalize(),
deny_label=i18n['do_not.install'].capitalize()) deny_label=i18n['do_not.install'].capitalize())
@@ -34,14 +34,14 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
return {o.value for o in view_opts.values} return {o.value for o in view_opts.values}
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> bool: def request_install_missing_deps(pkgname: str, deps: List[Tuple[str,str]], watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(bold(pkgname))) msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(name=bold(pkgname), deps=bold(len(deps))))
opts = [] opts = []
for p, m in pkg_mirrors.items(): for dep in deps:
op = InputOption('{} ( {}: {} )'.format(p, i18n['repository'], m.upper()), p) op = InputOption('{} ( {}: {} )'.format(dep[0], i18n['repository'], dep[1].upper()), dep[0])
op.read_only = True op.read_only = True
op.icon_path = _get_mirror_icon(m) op.icon_path = _get_mirror_icon(dep[1])
opts.append(op) opts.append(op)
comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts))

View File

@@ -6,7 +6,7 @@ import subprocess
import time import time
from pathlib import Path from pathlib import Path
from threading import Thread from threading import Thread
from typing import List, Set, Type from typing import List, Set, Type, Tuple
import requests import requests
@@ -22,6 +22,7 @@ from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, r
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions, \ from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions, \
gpg, URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH gpg, URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH
from bauh.gems.arch.aur import AURClient from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.depedencies import DependenciesAnalyser
from bauh.gems.arch.mapper import ArchDataMapper from bauh.gems.arch.mapper import ArchDataMapper
from bauh.gems.arch.model import ArchPackage from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer
@@ -62,6 +63,7 @@ class ArchManager(SoftwareManager):
self.categories_mapper = CategoriesDownloader('AUR', context.http_client, context.logger, self, self.context.disk_cache, self.categories_mapper = CategoriesDownloader('AUR', context.http_client, context.logger, self, self.context.disk_cache,
URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH) URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH)
self.categories = {} self.categories = {}
self.deps_analyser = DependenciesAnalyser(self.aur_client)
def _upgrade_search_result(self, apidata: dict, installed_pkgs: dict, downgrade_enabled: bool, res: SearchResult, disk_loader: DiskCacheLoader): def _upgrade_search_result(self, apidata: dict, installed_pkgs: dict, downgrade_enabled: bool, res: SearchResult, disk_loader: DiskCacheLoader):
app = self.mapper.map_api_data(apidata, installed_pkgs['not_signed'], self.categories) app = self.mapper.map_api_data(apidata, installed_pkgs['not_signed'], self.categories)
@@ -219,7 +221,7 @@ class ArchManager(SoftwareManager):
break break
watcher.change_substatus(self.i18n['arch.downgrade.install_older']) watcher.change_substatus(self.i18n['arch.downgrade.install_older'])
return self._make_pkg(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True) return self._build(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True)
else: else:
watcher.show_message(title=self.i18n['arch.downgrade.error'], watcher.show_message(title=self.i18n['arch.downgrade.error'],
body=self.i18n['arch.downgrade.impossible'].format(pkg.name), body=self.i18n['arch.downgrade.impossible'].format(pkg.name),
@@ -308,11 +310,17 @@ class ArchManager(SoftwareManager):
srcinfo = self.aur_client.get_src_info(pkg.name) srcinfo = self.aur_client.get_src_info(pkg.name)
if srcinfo: if srcinfo:
if srcinfo.get('makedepends'):
info['12_makedepends'] = srcinfo['makedepends']
if srcinfo.get('depends'): if srcinfo.get('depends'):
info['11_dependson'] = srcinfo['depends'] info['13_dependson'] = srcinfo['depends']
if srcinfo.get('optdepends'): if srcinfo.get('optdepends'):
info['12_optdepends'] = srcinfo['optdepends'] info['14_optdepends'] = srcinfo['optdepends']
if srcinfo.get('checkdepends'):
info['15_checkdepends'] = srcinfo['checkdepends']
if pkg.pkgbuild: if pkg.pkgbuild:
info['00_pkg_build'] = pkg.pkgbuild info['00_pkg_build'] = pkg.pkgbuild
@@ -355,34 +363,34 @@ class ArchManager(SoftwareManager):
if os.path.exists(temp_dir): if os.path.exists(temp_dir):
shutil.rmtree(temp_dir) shutil.rmtree(temp_dir)
def _install_deps(self, pkg_mirrors: dict, root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str: def _install_deps(self, deps: List[Tuple[str, str]], root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
""" """
:param pkg_mirrors: :param pkgs_repos:
:param root_password: :param root_password:
:param handler: :param handler:
:return: not installed dependency :return: not installed dependency
""" """
progress_increment = int(100 / len(pkg_mirrors)) progress_increment = int(100 / len(deps))
progress = 0 progress = 0
self._update_progress(handler.watcher, 1, change_progress) self._update_progress(handler.watcher, 1, change_progress)
for pkgname, mirror in pkg_mirrors.items(): for dep in deps:
handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(pkgname, mirror)))) handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(dep[0], dep[1]))))
if mirror == 'aur': if dep[1] == 'aur':
installed = self._install_from_aur(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False) installed = self._install_from_aur(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
else: else:
installed = self._install(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=mirror, change_progress=False) installed = self._install(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=dep[1], change_progress=False)
if not installed: if not installed:
return pkgname return dep[0]
progress += progress_increment progress += progress_increment
self._update_progress(handler.watcher, progress, change_progress) self._update_progress(handler.watcher, progress, change_progress)
self._update_progress(handler.watcher, 100, change_progress) self._update_progress(handler.watcher, 100, change_progress)
def _map_mirrors(self, pkgnames: Set[str]) -> dict: def _map_repos(self, pkgnames: Set[str]) -> dict:
pkg_mirrors = pacman.get_mirrors(pkgnames) # getting mirrors set pkg_mirrors = pacman.get_repositories(pkgnames) # getting mirrors set
if len(pkgnames) != len(pkg_mirrors): # checking if any dep not found in the distro mirrors are from AUR if len(pkgnames) != len(pkg_mirrors): # checking if any dep not found in the distro mirrors are from AUR
nomirrors = {p for p in pkgnames if p not in pkg_mirrors} nomirrors = {p for p in pkgnames if p not in pkg_mirrors}
@@ -423,12 +431,14 @@ class ArchManager(SoftwareManager):
return True return True
def _make_pkg(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool: def _build(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
self._pre_download_source(pkgname, project_dir, handler.watcher) self._pre_download_source(pkgname, project_dir, handler.watcher)
self._update_progress(handler.watcher, 50, change_progress) self._update_progress(handler.watcher, 50, change_progress)
if not self._check_deps(pkgname, root_password, handler, project_dir):
check_subdeps = bool(int(os.getenv('BAUH_ARCH_CHECK_SUBDEPS', 1)))
if not self._handle_deps_and_keys(pkgname, root_password, handler, project_dir, check_subdeps=check_subdeps):
return False return False
# building main package # building main package
@@ -458,35 +468,74 @@ class ArchManager(SoftwareManager):
return False return False
def _check_deps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool: def _map_missing_deps(self, deps: List[str], watcher: ProcessWatcher, check_subdeps: bool = True) -> List[Tuple[str, str]]:
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in deps}
dep_repos = self._map_repos(depnames)
if len(depnames) != len(dep_repos): # checking if a dependency could not be found in any mirror
for dep in depnames:
if dep not in dep_repos:
message.show_dep_not_found(dep, self.i18n, watcher)
return
sorted_deps = [] # it will hold the proper order to install the missing dependencies
repo_deps, aur_deps = set(), set()
for dep, repo in dep_repos.items():
if repo == 'aur':
aur_deps.add(dep)
else:
repo_deps.add(dep)
if check_subdeps:
for deps in ((repo_deps, 'repo'), (aur_deps, 'aur')):
if deps[0]:
missing_subdeps = self.deps_analyser.get_missing_dependencies_from(deps[0], deps[1])
if missing_subdeps:
for dep in missing_subdeps:
if not dep[1]:
message.show_dep_not_found(dep[0], self.i18n, watcher)
return
for dep in missing_subdeps:
sorted_deps.append(dep)
for dep, repo in dep_repos.items():
if repo != 'aur':
sorted_deps.append((dep, repo))
for dep in aur_deps:
sorted_deps.append((dep, 'aur'))
return sorted_deps
def _handle_deps_and_keys(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str, check_subdeps: bool = True) -> bool:
handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname))) handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname)))
check_res = makepkg.check(pkgdir, handler) check_res = makepkg.check(pkgdir, handler)
if check_res: if check_res:
if check_res.get('missing_deps'): if check_res.get('missing_deps'):
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in check_res['missing_deps']} sorted_deps = self._map_missing_deps(check_res['missing_deps'], handler.watcher, check_subdeps=check_subdeps)
dep_mirrors = self._map_mirrors(depnames)
if len(depnames) != len(dep_mirrors): # checking if a dependency could not be found in any mirror if sorted_deps is None:
for dep in depnames: return False
if dep not in dep_mirrors:
message.show_dep_not_found(dep, self.i18n, handler.watcher)
return False
handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname))) handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname)))
if not confirmation.request_install_missing_deps(pkgname, dep_mirrors, handler.watcher, self.i18n): if not confirmation.request_install_missing_deps(pkgname, sorted_deps, handler.watcher, self.i18n):
handler.watcher.print(self.i18n['action.cancelled']) handler.watcher.print(self.i18n['action.cancelled'])
return False return False
dep_not_installed = self._install_deps(dep_mirrors, root_password, handler, change_progress=False) dep_not_installed = self._install_deps(sorted_deps, root_password, handler, change_progress=False)
if dep_not_installed: if dep_not_installed:
message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n) message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n)
return False return False
# it is necessary to re-check because missing PGP keys are only notified when there are none missing # it is necessary to re-check because missing PGP keys are only notified when there are none missing
return self._check_deps(pkgname, root_password, handler, pkgdir) return self._handle_deps_and_keys(pkgname, root_password, handler, pkgdir, check_subdeps=False)
if check_res.get('gpg_key'): if check_res.get('gpg_key'):
if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.unknown_key.title'], if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.unknown_key.title'],
@@ -520,7 +569,7 @@ class ArchManager(SoftwareManager):
if not to_install: if not to_install:
return True return True
pkg_mirrors = self._map_mirrors(to_install) pkg_mirrors = self._map_repos(to_install)
if pkg_mirrors: if pkg_mirrors:
final_optdeps = {dep: {'desc': odeps.get(dep), 'mirror': pkg_mirrors.get(dep)} for dep, mirror in pkg_mirrors.items()} final_optdeps = {dep: {'desc': odeps.get(dep), 'mirror': pkg_mirrors.get(dep)} for dep, mirror in pkg_mirrors.items()}
@@ -642,15 +691,15 @@ class ArchManager(SoftwareManager):
if uncompress: if uncompress:
uncompress_dir = '{}/{}'.format(app_build_dir, pkgname) uncompress_dir = '{}/{}'.format(app_build_dir, pkgname)
return self._make_pkg(pkgname=pkgname, return self._build(pkgname=pkgname,
maintainer=maintainer, maintainer=maintainer,
root_password=root_password, root_password=root_password,
handler=handler, handler=handler,
build_dir=app_build_dir, build_dir=app_build_dir,
project_dir=uncompress_dir, project_dir=uncompress_dir,
dependency=dependency, dependency=dependency,
skip_optdeps=skip_optdeps, skip_optdeps=skip_optdeps,
change_progress=change_progress) change_progress=change_progress)
finally: finally:
if os.path.exists(app_build_dir): if os.path.exists(app_build_dir):
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', app_build_dir]))) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', app_build_dir])))

View File

@@ -0,0 +1,95 @@
from threading import Thread
from typing import Set, List, Tuple
from bauh.gems.arch import pacman
from bauh.gems.arch.aur import AURClient
class DependenciesAnalyser:
def __init__(self, aur_client: AURClient):
self.aur_client = aur_client
def _fill_mirror(self, name: str, output: List[Tuple[str, str]]):
mirror = pacman.read_repository_from_info(name)
if mirror:
output.append((name, mirror))
return
guess = pacman.guess_repository(name)
if guess:
output.append(guess)
return
aur_info = self.aur_client.get_src_info(name)
if aur_info:
output.append((name, 'aur'))
return
output.append((name, ''))
def get_missing_dependencies(self, names: Set[str], mirror: str = None) -> List[Tuple[str, str]]:
missing_names = pacman.check_missing(names)
if missing_names:
missing_root = []
threads = []
if not mirror:
for name in missing_names:
t = Thread(target=self._fill_mirror, args=(name, missing_root))
t.start()
threads.append(t)
for t in threads:
t.join()
threads.clear()
# checking if there is any unknown dependency:
for dep in missing_root:
if not dep[1]:
return missing_root
else:
for missing in missing_names:
missing_root.append((missing, mirror))
missing_sub = []
for dep in missing_root:
subdeps = self.aur_client.get_all_dependencies(dep[0]) if dep[1] == 'aur' else pacman.read_dependencies(dep[0])
if subdeps:
missing_subdeps = self.get_missing_dependencies(subdeps)
# checking if there is any unknown:
if missing_subdeps:
for subdep in missing_subdeps:
if not subdep[0]:
missing_sub.extend(missing_subdeps)
break
missing_sub.extend(missing_subdeps)
return [*missing_sub, *missing_root]
def get_missing_dependencies_from(self, names: Set[str], mirror: str) -> List[Tuple[str, str]]:
missing = []
for dep in names:
subdeps = self.aur_client.get_all_dependencies(dep) if mirror == 'aur' else pacman.read_dependencies(dep)
if subdeps:
missing_subdeps = self.get_missing_dependencies(subdeps)
if missing_subdeps:
missing.extend(missing_subdeps)
for subdep in missing_subdeps: # checking if there is any unknown:
if not subdep[0]:
return missing
return missing

View File

@@ -0,0 +1,5 @@
class PackageNotFoundException(Exception):
def __init__(self, name: str):
self.name = name

View File

@@ -3,9 +3,11 @@ from threading import Thread
from typing import List, Set, Tuple from typing import List, Set, Tuple
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess
from bauh.gems.arch.exceptions import PackageNotFoundException
RE_DEPS = re.compile(r'[\w\-_]+:[\s\w_\-\.]+\s+\[\w+\]') RE_DEPS = re.compile(r'[\w\-_]+:[\s\w_\-\.]+\s+\[\w+\]')
RE_OPTDEPS = re.compile(r'[\w\._\-]+\s*:') RE_OPTDEPS = re.compile(r'[\w\._\-]+\s*:')
RE_DEP_NOTFOUND = re.compile(r'error:.+\'(.+)\'')
def is_enabled() -> bool: def is_enabled() -> bool:
@@ -13,7 +15,7 @@ def is_enabled() -> bool:
return res and not res.strip().startswith('which ') return res and not res.strip().startswith('which ')
def get_mirror(pkg: str) -> Tuple[str, str]: def get_configured_repository(pkg: str) -> Tuple[str, str]:
res = run_cmd('pacman -Ss {}'.format(pkg)) res = run_cmd('pacman -Ss {}'.format(pkg))
if res: if res:
@@ -24,7 +26,7 @@ def get_mirror(pkg: str) -> Tuple[str, str]:
return data[1].split(' ')[0], data[0] return data[1].split(' ')[0], data[0]
def get_mirrors(pkgs: Set[str]) -> dict: def get_repositories(pkgs: Set[str]) -> dict:
pkgre = '|'.join(pkgs).replace('+', r'\+').replace('.', r'\.') pkgre = '|'.join(pkgs).replace('+', r'\+').replace('.', r'\.')
searchres = new_subprocess(['pacman', '-Ss', pkgre]).stdout searchres = new_subprocess(['pacman', '-Ss', pkgre]).stdout
@@ -41,7 +43,7 @@ def get_mirrors(pkgs: Set[str]) -> dict:
if not_found: # if there are some packages not found, try to find via the single method: if not_found: # if there are some packages not found, try to find via the single method:
for dep in not_found: for dep in not_found:
mirror_data = get_mirror(dep) mirror_data = get_configured_repository(dep)
if mirror_data: if mirror_data:
mirrors[mirror_data[0]] = mirror_data[1] mirrors[mirror_data[0]] = mirror_data[1]
@@ -49,7 +51,7 @@ def get_mirrors(pkgs: Set[str]) -> dict:
return mirrors return mirrors
def is_available_from_mirrors(pkg_name: str) -> bool: def is_available_in_repositories(pkg_name: str) -> bool:
return bool(run_cmd('pacman -Ss ' + pkg_name)) return bool(run_cmd('pacman -Ss ' + pkg_name))
@@ -236,3 +238,141 @@ def list_ignored_packages(config_path: str = '/etc/pacman.conf') -> Set[str]:
pacman_conf.terminate() pacman_conf.terminate()
grep.terminate() grep.terminate()
return ignored return ignored
def check_missing(names: Set[str]) -> Set[str]:
installed = new_subprocess(['pacman', '-Qq', *names])
not_installed = set()
for o in installed.stderr:
if o:
err_line = o.decode()
if err_line:
not_found = RE_DEP_NOTFOUND.findall(err_line)
if not_found:
not_installed.update(not_found)
return not_installed
def read_repository_from_info(name: str) -> str:
info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in info.stderr:
if o:
err_line = o.decode()
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
return
mirror = None
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=info.stdout).stdout:
if o:
line = o.decode().strip()
if line:
mirror = line
return mirror
def guess_repository(name: str) -> Tuple[str, str]:
res = run_cmd('pacman -Ss {}'.format(name.split('=')[0] if '=' in name else name))
if res:
lines = res.split('\n')
if lines:
data = lines[0].split('/')
return data[1].split(' ')[0], data[0]
def read_dependencies(name: str) -> Set[str]:
dep_info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in dep_info.stderr:
if o:
err_line = o.decode()
if err_line:
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
raise PackageNotFoundException(name)
depends_on = set()
for out in new_subprocess(['grep', '-Po', 'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
if out:
line = out.decode().strip()
if line:
depends_on.update(line.split(' '))
return depends_on
"""
def check_uninstalled(names: List[str], stop_recursion_if_no_mirror: bool = True) -> Dict[str, str]:
missing = {}
installed = new_subprocess(['pacman', '-Qq', *names])
not_installed = []
for o in installed.stderr:
if o:
err_line = o.decode()
if err_line:
not_found = RE_DEP_NOTFOUND.findall(err_line)
if not_found:
not_installed.extend(not_found)
if not_installed:
missing_mirrors = False
for dep in not_installed:
pkgname = dep
not_installed_info = new_subprocess(['pacman', '-Si', dep])
mirror = None
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=not_installed_info.stdout).stdout:
if o:
line = o.decode().strip()
if line:
mirror = line
if not mirror:
mirror_data = get_configured_repository(dep)
if mirror_data:
pkgname = mirror_data[0]
mirror = mirror_data[1]
missing[pkgname] = mirror
if mirror is None:
missing_mirrors = True
if stop_recursion_if_no_mirror and missing_mirrors:
return missing
subdeps_mirrors = dict()
for pkg in missing:
subdeps_mirrors.update(check_uninstalled(pkg))
missing.update(subdeps_mirrors, stop_recursion_if_no_mirror=stop_recursion_if_no_mirror)
return missing
"""

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=té un conflicte amb
arch.install.conflict.popup.title=Sha detectat un conflicte arch.install.conflict.popup.title=Sha detectat un conflicte
arch.install.conflict.popup.body=Les aplicacions {} estan en conflicte. Heu de desinstal·lar una per a instal·lar laltra. Voleu continuar? arch.install.conflict.popup.body=Les aplicacions {} estan en conflicte. Heu de desinstal·lar una per a instal·lar laltra. Voleu continuar?
arch.missing_deps.title=Dependències mancants arch.missing_deps.title=Dependències mancants
arch.missing_deps.body=Shan dinstal·lar les dependències següents abans de continuar amb la instal·lació de {} arch.missing_deps.body=Shan dinstal·lar les {deps} dependències següents abans de continuar amb la instal·lació de {name}
arch.downgrade.error=Error arch.downgrade.error=Error
arch.downgrade.impossible=No sha pogut revertir la versió de {} arch.downgrade.impossible=No sha pogut revertir la versió de {}
aur.history.1_version=versió aur.history.1_version=versió
@@ -69,8 +69,10 @@ aur.info.09_last_modified=darrera modificació
aur.info.10_url=url de baixada aur.info.10_url=url de baixada
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependències aur.info.12_makedepends=dependències de compilació
aur.info.12_optdepends=dependències opcionals aur.info.13_dependson=dependències dinstal·lació
aur.info.14_optdepends=dependències opcionals
aur.info.15_checkdepends=dependències de verificació
aur.info.14_installed_files=Fitxers instal·lats aur.info.14_installed_files=Fitxers instal·lats
arch.install.dep_not_found.title=No sha trobat la dependència arch.install.dep_not_found.title=No sha trobat la dependència
arch.install.dep_not_found.body=No sha trobat la dependència requerida {} a lAUR ni als servidors rèplica per defecte. Sha cancel·lat la instal·lació. arch.install.dep_not_found.body=No sha trobat la dependència requerida {} a lAUR ni als servidors rèplica per defecte. Sha cancel·lat la instal·lació.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR Pakete werden von einer unabhängigen Nutzergemeinsc
arch.install.conflict.popup.title=Konflikt entdeckt arch.install.conflict.popup.title=Konflikt entdeckt
arch.install.conflict.popup.body=Die Anwendungen {} können nicht gleichzeitig installiert sein. Du musst eine deinstallieren um die andere zu installieren. Fortfahren? arch.install.conflict.popup.body=Die Anwendungen {} können nicht gleichzeitig installiert sein. Du musst eine deinstallieren um die andere zu installieren. Fortfahren?
arch.missing_deps.title=Fehlende Abhängigkeiten arch.missing_deps.title=Fehlende Abhängigkeiten
arch.missing_deps.body=Die folgenden Abhängigkeiten müssten installiert sein, bevor mit der {} Installation fortgefahren werden kann arch.missing_deps.body=Die folgenden {deps} Abhängigkeiten müssten installiert sein, bevor mit der {name} Installation fortgefahren werden kann
arch.downgrade.error=Fehler arch.downgrade.error=Fehler
arch.downgrade.impossible=Downgrade von {} ist nicht möglich arch.downgrade.impossible=Downgrade von {} ist nicht möglich
aur.history.1_version=Version aur.history.1_version=Version
@@ -37,8 +37,10 @@ aur.info.09_last_modified=Zuletzt verändert
aur.info.10_url=url zum Herunterladen aur.info.10_url=url zum Herunterladen
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=Abhängigkeiten aur.info.12_makedepends=Kompilierungsabhängigkeiten
aur.info.12_optdepends=optionale Abhängigkeiten aur.info.13_dependson=Installationsabhängigkeiten
aur.info.14_optdepends=optionale Abhängigkeiten
aur.info.15_checkdepends=Überprüfungsabhängigkeiten
aur.info.14_installed_files=Installationsdateien aur.info.14_installed_files=Installationsdateien
arch.install.dep_not_found.title=Abhängigkeit nicht gefunden arch.install.dep_not_found.title=Abhängigkeit nicht gefunden
arch.install.dep_not_found.body=Nötige Abhängigkeit {} wurde weder im AUR noch in den standard Spiegelservern gefunden. Installation abgebrochen arch.install.dep_not_found.body=Nötige Abhängigkeit {} wurde weder im AUR noch in den standard Spiegelservern gefunden. Installation abgebrochen

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR packages are maintained by an independent user commu
arch.install.conflict.popup.title=Conflict detected arch.install.conflict.popup.title=Conflict detected
arch.install.conflict.popup.body=The applications {} are in conflict. You must uninstall one to install the other. Continue ? arch.install.conflict.popup.body=The applications {} are in conflict. You must uninstall one to install the other. Continue ?
arch.missing_deps.title=Missing dependencies arch.missing_deps.title=Missing dependencies
arch.missing_deps.body=The following dependencies must be installed before the {} installation continues arch.missing_deps.body=The following {deps} dependencies must be installed before the {name} installation continues
arch.downgrade.error=Error arch.downgrade.error=Error
arch.downgrade.impossible=It is not possible to downgrade {} arch.downgrade.impossible=It is not possible to downgrade {}
aur.history.1_version=version aur.history.1_version=version
@@ -37,8 +37,10 @@ aur.info.09_last_modified=last modified
aur.info.10_url=url download aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencies aur.info.12_makedepends=compilation dependencies
aur.info.12_optdepends=optional dependencies aur.info.13_dependson=installation dependencies
aur.info.14_optdepends=optional dependencies
aur.info.15_checkdepends=checking dependencies
aur.info.14_installed_files=Installed files aur.info.14_installed_files=Installed files
arch.install.dep_not_found.title=Dependency not found arch.install.dep_not_found.title=Dependency not found
arch.install.dep_not_found.body=Required dependency {} was not found in AUR nor in default mirrors. Installation cancelled. arch.install.dep_not_found.body=Required dependency {} was not found in AUR nor in default mirrors. Installation cancelled.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflicta
arch.install.conflict.popup.title=Conflicto detectado arch.install.conflict.popup.title=Conflicto detectado
arch.install.conflict.popup.body=Los aplicativos {} estan en conflicto. Debe desinstalar uno para instalar el otro. ¿Continuar? arch.install.conflict.popup.body=Los aplicativos {} estan en conflicto. Debe desinstalar uno para instalar el otro. ¿Continuar?
arch.missing_deps.title=Dependencias faltantes arch.missing_deps.title=Dependencias faltantes
arch.missing_deps.body=Deben instalarse las siguientes dependencias antes de continuar la instalación de {} arch.missing_deps.body=Deben instalarse las siguientes {deps} dependencias antes de continuar la instalación de {name}
arch.downgrade.error=Error arch.downgrade.error=Error
arch.downgrade.impossible=No es posible revertir la versión de {} arch.downgrade.impossible=No es posible revertir la versión de {}
aur.history.1_version=versión aur.history.1_version=versión
@@ -69,8 +69,10 @@ aur.info.09_last_modified=última modificación
aur.info.10_url=url download aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencias aur.info.12_makedepends=dependencias para compilacion
aur.info.12_optdepends=dependencias opcionales aur.info.13_dependson=dependencias para instalación
aur.info.14_optdepends=dependencias opcionales
aur.info.15_checkdepends=dependencias para verificación
aur.info.14_installed_files=Archivos instalados aur.info.14_installed_files=Archivos instalados
arch.install.dep_not_found.title=Dependencia no encontrada arch.install.dep_not_found.title=Dependencia no encontrada
arch.install.dep_not_found.body=No se encontró la dependencia requerida {} en AUR ni en los espejos predeterminados. Instalación cancelada. arch.install.dep_not_found.body=No se encontró la dependencia requerida {} en AUR ni en los espejos predeterminados. Instalación cancelada.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=I pacchetti AUR sono gestiti da una comunità di utenti
arch.install.conflict.popup.title=Conflitto rilevato arch.install.conflict.popup.title=Conflitto rilevato
arch.install.conflict.popup.body=Le applicazioni {} sono in conflitto. È necessario disinstallarne una per installare l'altra. Continua ? arch.install.conflict.popup.body=Le applicazioni {} sono in conflitto. È necessario disinstallarne una per installare l'altra. Continua ?
arch.missing_deps.title=Dipendenze mancanti arch.missing_deps.title=Dipendenze mancanti
arch.missing_deps.body=Le seguenti dipendenze devono essere installate prima che l'installazione di {} continui arch.missing_deps.body=Le seguenti {deps} dipendenze devono essere installate prima che l'installazione di {name} continui
arch.downgrade.error=Errore arch.downgrade.error=Errore
arch.downgrade.impossible=Non è possibile effettuare il downgrade {} arch.downgrade.impossible=Non è possibile effettuare il downgrade {}
aur.history.1_version=versione aur.history.1_version=versione
@@ -37,8 +37,10 @@ aur.info.09_last_modified=ultima modifica
aur.info.10_url=url di download aur.info.10_url=url di download
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dipendenze aur.info.12_makedepends=dipendenze di compilazione
aur.info.12_optdepends=dipendenze opzionali aur.info.13_dependson=dipendenze di installazione
aur.info.14_optdepends=dipendenze opzionali
aur.info.15_checkdepends=dipendenze di verifica
aur.info.14_installed_files=File installati aur.info.14_installed_files=File installati
arch.install.dep_not_found.title=Dipendenza non trovata arch.install.dep_not_found.title=Dipendenza non trovata
arch.install.dep_not_found.body=La dipendenza richiesta {} non è stata trovata in AUR né nei mirror predefiniti. Installazione annullata. arch.install.dep_not_found.body=La dipendenza richiesta {} non è stata trovata in AUR né nei mirror predefiniti. Installazione annullata.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflita
arch.install.conflict.popup.title=Conflito detectado arch.install.conflict.popup.title=Conflito detectado
arch.install.conflict.popup.body=Os aplicativos {} estão em conflito. Você precisa desinstalar um para instalar o outro. Continuar ? arch.install.conflict.popup.body=Os aplicativos {} estão em conflito. Você precisa desinstalar um para instalar o outro. Continuar ?
arch.missing_deps.title=Dependências ausentes arch.missing_deps.title=Dependências ausentes
arch.missing_deps.body=As seguintes dependências devem ser instaladas antes de continuar com a instalação de {} arch.missing_deps.body=As seguintes {deps} dependências devem ser instaladas antes de continuar com a instalação de {name}
arch.downgrade.error=Erro arch.downgrade.error=Erro
arch.downgrade.impossible=Não é possível reverter a versão de {} arch.downgrade.impossible=Não é possível reverter a versão de {}
aur.history.1_version=versão aur.history.1_version=versão
@@ -69,8 +69,10 @@ aur.info.09_last_modified=última modificação
aur.info.10_url=url download aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependências aur.info.12_makedepends=dependências para compilação
aur.info.12_optdepends=dependências opcionais aur.info.13_dependson=dependências para instalação
aur.info.14_optdepends=dependências opcionais
aur.info.15_checkdepends=dependências para verificação
aur.info.14_installed_files=Arquivos instalados aur.info.14_installed_files=Arquivos instalados
arch.install.dep_not_found.title=Dependência não encontrada arch.install.dep_not_found.title=Dependência não encontrada
arch.install.dep_not_found.body=A dependência {} não foi encontrado no AUR nem nos espelhos padrões. Instalação cancelada. arch.install.dep_not_found.body=A dependência {} não foi encontrado no AUR nem nos espelhos padrões. Instalação cancelada.

View File

@@ -161,8 +161,11 @@ class SnapManager(SoftwareManager):
if channels: if channels:
opts = [InputOption(label=c[0], value=c[1]) for c in channels] opts = [InputOption(label=c[0], value=c[1]) for c in channels]
channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0]) channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0])
body = '<p>{}.</p>'.format(self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name)))
body += '<p>{}:</p>'.format(self.i18n['snap.install.available_channels.help'])
if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'], if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'],
body=self.i18n['snap.install.available_channels.body'].format(bold(self.i18n['stable']), bold(pkg.name)) + ':', body=body,
components=[channel_select], components=[channel_select],
confirmation_label=self.i18n['continue'], confirmation_label=self.i18n['continue'],
deny_label=self.i18n['cancel']): deny_label=self.i18n['cancel']):

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Sembla que lAPI de {} no està disponible
snap.action.refresh.status=Sestà actualitzant snap.action.refresh.status=Sestà actualitzant
snap.action.refresh.label=Actualitza snap.action.refresh.label=Actualitza
snap.install.available_channels.title=Canals disponibles snap.install.available_channels.title=Canals disponibles
snap.install.available_channels.body=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents (seleccioneu-ne un si voleu continuar) snap.install.available_channels.message=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents
snap.install.available_channels.help=Seleccioneu-ne un si voleu continuar

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=Es scheint als ob die {} API aktuell nicht ve
snap.action.refresh.status=Erneuern snap.action.refresh.status=Erneuern
snap.action.refresh.label=Erneuern snap.action.refresh.label=Erneuern
snap.install.available_channels.title=Verfügbare Channels snap.install.available_channels.title=Verfügbare Channels
snap.install.available_channels.body=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende (wähle einen aus um fortzufahren) snap.install.available_channels.message=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende
snap.install.available_channels.help=Wähle einen aus um fortzufahren
snap.info.commands=Kommandos snap.info.commands=Kommandos
snap.info.contact=Kontakt snap.info.contact=Kontakt
snap.info.description=Beschreibung snap.info.description=Beschreibung

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=It seems the {} API is unavailable at the mom
snap.action.refresh.status=Refreshing snap.action.refresh.status=Refreshing
snap.action.refresh.label=Refresh snap.action.refresh.label=Refresh
snap.install.available_channels.title=Available channels snap.install.available_channels.title=Available channels
snap.install.available_channels.body=There is no {} channel available for {}. But there are these below ( select one if you want to continue ) snap.install.available_channels.message=There is no {} channel available for {}. But there are these below
snap.install.available_channels.help=Select one if you want to continue
snap.info.commands=commands snap.info.commands=commands
snap.info.contact=contact snap.info.contact=contact
snap.info.description=description snap.info.description=description

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que la API de {} no está disponible e
snap.action.refresh.status=Actualizando snap.action.refresh.status=Actualizando
snap.action.refresh.label=Actualizar snap.action.refresh.label=Actualizar
snap.install.available_channels.title=Canales disponibles snap.install.available_channels.title=Canales disponibles
snap.install.available_channels.body=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo ( seleccione uno si desea continuar ) snap.install.available_channels.message=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo
snap.install.available_channels.help=Seleccione uno si desea continuar

View File

@@ -5,4 +5,5 @@ snap.notifications.api.unavailable=Sembra che l'API {} non sia al momento dispon
snap.action.refresh.status=Ripristinare snap.action.refresh.status=Ripristinare
snap.action.refresh.label=Ripristina snap.action.refresh.label=Ripristina
snap.install.available_channels.title=Canali disponibili snap.install.available_channels.title=Canali disponibili
snap.install.available_channels.body=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto (selezionane uno se vuoi continuare) snap.install.available_channels.message=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto
snap.install.available_channels.help=Selezionane uno se vuoi continuare

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que a API de {} está indisponível no
snap.action.refresh.status=Atualizando snap.action.refresh.status=Atualizando
snap.action.refresh.label=Atualizar snap.action.refresh.label=Atualizar
snap.install.available_channels.title=Canais disponíveis snap.install.available_channels.title=Canais disponíveis
snap.install.available_channels.body=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo ( selecione algum se quiser continuar ) snap.install.available_channels.message=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo
snap.install.available_channels.help=Selecione algum se quiser continuar

View File

@@ -1,6 +1,7 @@
from typing import List from typing import List
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent
from bauh.view.qt import css from bauh.view.qt import css
@@ -10,7 +11,8 @@ from bauh.view.util.translation import I18n
class ConfirmationDialog(QMessageBox): class ConfirmationDialog(QMessageBox):
def __init__(self, title: str, body: str, i18n: I18n, components: List[ViewComponent] = None, confirmation_label: str = None, deny_label: str = None): def __init__(self, title: str, body: str, i18n: I18n, screen_size: QSize, components: List[ViewComponent] = None,
confirmation_label: str = None, deny_label: str = None):
super(ConfirmationDialog, self).__init__() super(ConfirmationDialog, self).__init__()
self.setWindowTitle(title) self.setWindowTitle(title)
self.setStyleSheet('QLabel { margin-right: 25px; }') self.setStyleSheet('QLabel { margin-right: 25px; }')
@@ -26,8 +28,15 @@ class ConfirmationDialog(QMessageBox):
self.layout().addWidget(QLabel(body), 0, 1) self.layout().addWidget(QLabel(body), 0, 1)
if components: if components:
comps_container = QWidget(parent=self) scroll = QScrollArea(self)
scroll.setFrameShape(QFrame.NoFrame)
scroll.setWidgetResizable(True)
comps_container = QWidget()
comps_container.setLayout(QVBoxLayout()) comps_container.setLayout(QVBoxLayout())
scroll.setWidget(comps_container)
height = 0
for idx, comp in enumerate(components): for idx, comp in enumerate(components):
if isinstance(comp, SingleSelectComponent): if isinstance(comp, SingleSelectComponent):
@@ -37,9 +46,16 @@ class ConfirmationDialog(QMessageBox):
else: else:
raise Exception("Cannot render instances of " + comp.__class__.__name__) raise Exception("Cannot render instances of " + comp.__class__.__name__)
height += inst.sizeHint().height()
comps_container.layout().addWidget(inst) comps_container.layout().addWidget(inst)
self.layout().addWidget(comps_container, 1, 1) height = height if height < screen_size.height() / 2 else height / 2
if height < 100:
height = 100
scroll.setFixedHeight(height)
self.layout().addWidget(scroll, 1, 1)
self.exec_() self.exec_()

View File

@@ -379,7 +379,8 @@ class ManageWindow(QWidget):
i18n=self.i18n, i18n=self.i18n,
components=msg['components'], components=msg['components'],
confirmation_label=msg['confirmation_label'], confirmation_label=msg['confirmation_label'],
deny_label=msg['deny_label']) deny_label=msg['deny_label'],
screen_size=self.screen_size)
res = diag.is_confirmed() res = diag.is_confirmed()
self.thread_animate_progress.animate() self.thread_animate_progress.animate()
self.signal_user_res.emit(res) self.signal_user_res.emit(res)