[feature][ui] it is possible to view details of some initialization tasks by clicking on their icons

This commit is contained in:
Vinicius Moreira
2020-05-03 12:02:12 -03:00
parent e2e40747a5
commit 600f0068bf
25 changed files with 184 additions and 44 deletions

View File

@@ -102,6 +102,15 @@ class TaskManager:
"""
pass
def update_output(self, task_id: str, output: str):
"""
updates the task output
:param task_id:
:param output:
:return:
"""
pass
def finish_task(self, task_id: str):
"""
marks a task as finished

View File

@@ -500,8 +500,7 @@ def get_current_mirror_countries() -> List[str]:
def is_mirrors_available() -> bool:
res = run_cmd('which pacman-mirrors', print_error=False)
return res and not res.strip().startswith('which ')
return bool(run_cmd('which pacman-mirrors', print_error=False))
def get_update_size(pkgs: List[str]) -> Dict[str, int]: # bytes:

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Llegits
arch.task.disk_cache.reading=Determinant els paquets instal·lats
arch.task.mirrors=Refreshing mirrors
arch.task.optimizing=Optimizing {}
arch.task.sync_databases.waiting=Waiting for mirrors refreshing
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status=Actualitzen {}
arch.uncompressing.package=Sestà descomprimint el paquet
arch.uninstall.clean_cached.error=No s'ha pogut eliminar {} versions antigues que es troba al disc

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Read
arch.task.disk_cache.reading=Determining installed packages
arch.task.mirrors=Refreshing mirrors
arch.task.optimizing=Optimizing {}
arch.task.sync_databases.waiting=Waiting for mirrors refreshing
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status=Updating {}
arch.uncompressing.package=Paket entpacken
arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Read
arch.task.disk_cache.reading=Determining installed packages
arch.task.mirrors=Refreshing mirrors
arch.task.optimizing=Optimizing {}
arch.task.sync_databases.waiting=Waiting for mirrors refreshing
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status=Updating {}
arch.uncompressing.package=Uncompressing the package
arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Lidos
arch.task.disk_cache.reading=Determinando los paquetes instalados
arch.task.mirrors=Actualizando espejos
arch.task.optimizing=Optimizando {}
arch.task.sync_databases.waiting=Esperando por actualización de espejos
arch.task.sync_databases.waiting=Esperando por {}
arch.task.sync_sb.status=Actualizando {}
arch.uncompressing.package=Descomprimindo el paquete
arch.uninstall.clean_cached.error=No fue posible eliminar versiones antiguas de {} encontradas en disco

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Letti
arch.task.disk_cache.reading=Determinando i pacchetti installati
arch.task.mirrors=Aggiornando i mirror
arch.task.optimizing=Ottimizzando {}
arch.task.sync_databases.waiting=In attesa dell'aggiornamento dei mirror
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status=Aggiornando {}
arch.uncompressing.package=Non comprimere il pacchetto
arch.uninstall.clean_cached.error=Non è stato possibile rimuovere le vecchie {} versioni trovate sul disco

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Lidos
arch.task.disk_cache.reading=Determinando pacotes instalados
arch.task.mirrors=Atualizando espelhos
arch.task.optimizing=Otimizando {}
arch.task.sync_databases.waiting=Aguardando atualização de espelhos
arch.task.sync_databases.waiting=Aguardando por {}
arch.task.sync_sb.status=Atualizando {}
arch.uncompressing.package=Descompactando o pacote
arch.uninstall.clean_cached.error=Não foi possível remover versões antigas de {} encontradas em disco

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=Read
arch.task.disk_cache.reading=Determining installed packages
arch.task.mirrors=Обновление зеркал
arch.task.optimizing=Optimizing {}
arch.task.sync_databases.waiting=Ожидание обновления зеркал
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status=Updating {}
arch.uncompressing.package=Распаковка пакета
arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk

View File

@@ -168,7 +168,7 @@ arch.task.disk_cache.prepared=oku
arch.task.disk_cache.reading=Kurulu paketler belirleniyor
arch.task.mirrors=Yansılar yenileniyor
arch.task.optimizing=Uygun hale getiriliyor {}
arch.task.sync_databases.waiting=Yansıların yenilenmesi bekleniyor
arch.task.sync_databases.waiting=Waiting for {}
arch.task.sync_sb.status={} güncelleniyor
arch.uncompressing.package=Paket arşivden çıkarılıyor
arch.uninstall.clean_cached.error=Diskte bulunan eski {} sürümleri kaldırılamadı

View File

@@ -292,21 +292,24 @@ class RefreshMirrors(Thread):
self.task_id = "arch_mirrors"
self.sort_limit = sort_limit
def _notify_output(self, output: str):
self.taskman.update_output(self.task_id, output)
def run(self):
self.logger.info("Refreshing mirrors")
self.taskman.register_task(self.task_id, self.i18n['arch.task.mirrors'], get_icon_path())
self.logger.info("Refreshing mirrors")
handler = ProcessHandler()
try:
self.taskman.update_progress(self.task_id, 10, '')
success, output = handler.handle_simple(pacman.refresh_mirrors(self.root_password))
success, output = handler.handle_simple(pacman.refresh_mirrors(self.root_password), output_handler=self._notify_output)
if success:
if self.sort_limit is not None and self.sort_limit >= 0:
self.taskman.update_progress(self.task_id, 50, self.i18n['arch.custom_action.refresh_mirrors.status.updating'])
try:
handler.handle_simple(pacman.sort_fastest_mirrors(self.root_password, self.sort_limit))
handler.handle_simple(pacman.sort_fastest_mirrors(self.root_password, self.sort_limit), output_handler=self._notify_output)
except:
self.logger.error("Could not sort mirrors by speed")
traceback.print_exc()
@@ -340,7 +343,7 @@ class SyncDatabases(Thread):
self.taskman.register_task(self.task_id, self.i18n['arch.sync_databases.substatus'], get_icon_path())
if self.refresh_mirrors and self.refresh_mirrors.is_alive():
self.taskman.update_progress(self.task_id, 0, self.i18n['arch.task.sync_databases.waiting'])
self.taskman.update_progress(self.task_id, 0, self.i18n['arch.task.sync_databases.waiting'].format('"{}"'.format(self.i18n['arch.task.mirrors'])))
self.refresh_mirrors.join()
progress = 10
@@ -357,20 +360,25 @@ class SyncDatabases(Thread):
for o in p.stdout:
line = o.decode().strip()
if line and line.startswith('downloading'):
db = line.split(' ')[1].strip()
if line:
self.task_man.update_output(self.task_id, line)
if line.startswith('downloading'):
db = line.split(' ')[1].strip()
if last_db is None or last_db != db:
last_db = db
dbs_read += 1
progress = dbs_read * inc
else:
progress += 0.25
if last_db is None or last_db != db:
last_db = db
dbs_read += 1
progress = dbs_read * inc
else:
progress += 0.25
self.taskman.update_progress(self.task_id, progress, self.i18n['arch.task.sync_sb.status'].format(db))
self.taskman.update_progress(self.task_id, progress, self.i18n['arch.task.sync_sb.status'].format(db))
for o in p.stderr:
o.decode()
line = o.decode().strip()
if line:
self.task_man.update_output(self.task_id, line)
p.wait()

View File

@@ -5,9 +5,9 @@ from functools import reduce
from typing import Tuple
from PyQt5.QtCore import QSize, Qt, QThread, pyqtSignal, QCoreApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtGui import QIcon, QCursor
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy, QTableWidget, QHeaderView, QPushButton, QToolBar, \
QProgressBar, QApplication
QProgressBar, QApplication, QPlainTextEdit, QToolButton, QHBoxLayout
from bauh import __app_name__
from bauh.api.abstract.context import ApplicationContext
@@ -27,6 +27,7 @@ class Prepare(QThread, TaskManager):
signal_finished = pyqtSignal(str)
signal_started = pyqtSignal()
signal_ask_password = pyqtSignal()
signal_output = pyqtSignal(str, str)
def __init__(self, context: ApplicationContext, manager: SoftwareManager, i18n: I18n):
super(Prepare, self).__init__()
@@ -63,6 +64,9 @@ class Prepare(QThread, TaskManager):
def update_progress(self, task_id: str, progress: float, substatus: str):
self.signal_update.emit(task_id, progress, substatus)
def update_output(self, task_id: str, output: str):
self.signal_output.emit(task_id, output)
def register_task(self, id_: str, label: str, icon_path: str):
self.signal_register.emit(id_, label, icon_path)
@@ -79,6 +83,7 @@ class CheckFinished(QThread):
self.finished = None
def run(self):
time.sleep(3)
while True:
if self.total is not None and self.finished is not None:
if self.total == self.finished:
@@ -117,8 +122,7 @@ class PreparePanel(QWidget, TaskManager):
signal_password_response = pyqtSignal(str, bool)
def __init__(self, context: ApplicationContext, manager: SoftwareManager, screen_size: QSize, i18n: I18n, manage_window: QWidget):
super(PreparePanel, self).__init__()
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
super(PreparePanel, self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
self.i18n = i18n
self.context = context
self.manage_window = manage_window
@@ -130,6 +134,7 @@ class PreparePanel(QWidget, TaskManager):
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
self.manager = manager
self.tasks = {}
self.output = {}
self.ntasks = 0
self.ftasks = 0
self.self_close = False
@@ -140,6 +145,7 @@ class PreparePanel(QWidget, TaskManager):
self.prepare_thread.signal_finished.connect(self.finish_task)
self.prepare_thread.signal_started.connect(self.start)
self.prepare_thread.signal_ask_password.connect(self.ask_root_password)
self.prepare_thread.signal_output.connect(self.update_output)
self.signal_password_response.connect(self.prepare_thread.set_password_reply)
self.check_thread = CheckFinished()
@@ -170,27 +176,60 @@ class PreparePanel(QWidget, TaskManager):
self.table.setHorizontalHeaderLabels(['' for _ in range(4)])
self.layout().addWidget(self.table)
toolbar = QToolBar()
self.textarea_output = QPlainTextEdit(self)
self.textarea_output.resize(self.table.size())
self.textarea_output.setStyleSheet("background: black; color: white;")
self.layout().addWidget(self.textarea_output)
self.textarea_output.setVisible(False)
self.textarea_output.setReadOnly(True)
self.textarea_output.setMaximumHeight(100)
self.current_output_task = None
self.bottom_widget = QWidget()
self.bottom_widget.setLayout(QHBoxLayout())
self.bottom_widget.layout().addStretch()
bt_hide_output = QPushButton('Hide details')
bt_hide_output.setStyleSheet('QPushButton { text-decoration: underline; border: 0px; background: none } ')
bt_hide_output.clicked.connect(self.hide_output)
bt_hide_output.setCursor(QCursor(Qt.PointingHandCursor))
self.bottom_widget.layout().addWidget(bt_hide_output)
self.bottom_widget.layout().addStretch()
self.layout().addWidget(self.bottom_widget)
self.bottom_widget.setVisible(False)
self.bt_bar = QToolBar()
self.bt_close = QPushButton(self.i18n['close'].capitalize())
self.bt_close.setCursor(QCursor(Qt.PointingHandCursor))
self.bt_close.clicked.connect(self.close)
self.bt_close.setVisible(False)
self.ref_bt_close = toolbar.addWidget(self.bt_close)
self.ref_bt_close = self.bt_bar.addWidget(self.bt_close)
toolbar.addWidget(new_spacer())
self.bt_bar.addWidget(new_spacer())
self.progress_bar = QProgressBar()
self.progress_bar.setStyleSheet(styles.PROGRESS_BAR)
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
self.progress_bar.setTextVisible(False)
self.progress_bar.setVisible(False)
self.ref_progress_bar = toolbar.addWidget(self.progress_bar)
toolbar.addWidget(new_spacer())
self.ref_progress_bar = self.bt_bar.addWidget(self.progress_bar)
self.bt_bar.addWidget(new_spacer())
self.bt_skip = QPushButton(self.i18n['prepare_panel.bt_skip.label'].capitalize())
self.bt_skip.clicked.connect(self.finish)
self.bt_skip.setEnabled(False)
toolbar.addWidget(self.bt_skip)
self.bt_bar.addWidget(self.bt_skip)
self.layout().addWidget(toolbar)
self.layout().addWidget(self.bt_bar)
def hide_output(self):
self.current_output_task = None
self.textarea_output.setVisible(False)
self.textarea_output.clear()
self.bottom_widget.setVisible(False)
self._resize_columns()
self.setFocus(Qt.NoFocusReason)
if not self.bt_bar.isVisible():
self.bt_bar.setVisible(True)
def ask_root_password(self):
root_pwd, ok = root.ask_root_password(self.context, self.i18n)
@@ -198,6 +237,7 @@ class PreparePanel(QWidget, TaskManager):
def _enable_skip_button(self):
self.bt_skip.setEnabled(True)
self.bt_skip.setCursor(QCursor(Qt.PointingHandCursor))
def _change_progress(self, value: int):
self.progress_bar.setValue(value)
@@ -235,14 +275,40 @@ class PreparePanel(QWidget, TaskManager):
task_row = self.ntasks - 1
lb_icon = QLabel()
lb_icon.setContentsMargins(10, 0, 10, 0)
lb_icon.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
icon_widget = QWidget()
icon_widget.setLayout(QHBoxLayout())
icon_widget.layout().setContentsMargins(10, 0, 10, 0)
bt_icon = QToolButton()
bt_icon.setEnabled(False)
bt_icon.setToolTip(self.i18n['prepare.bt_icon.no_output'])
bt_icon.setFixedSize(QSize(24, 24))
bt_icon.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
if icon_path:
lb_icon.setPixmap(QIcon(icon_path).pixmap(14, 14))
bt_icon.setIcon(QIcon(icon_path))
self.table.setCellWidget(task_row, 0, lb_icon)
def _show_output():
lines = self.output[id_]
if lines:
self.current_output_task = id_
self.textarea_output.clear()
self.textarea_output.setVisible(True)
for l in lines:
self.textarea_output.appendPlainText(l)
self.bottom_widget.setVisible(True)
self.setFocus(Qt.NoFocusReason)
if self.bt_bar.isVisible():
self.bt_bar.setVisible(False)
bt_icon.clicked.connect(_show_output)
icon_widget.layout().addWidget(bt_icon)
self.table.setCellWidget(task_row, 0, icon_widget)
lb_status = QLabel(label)
lb_status.setMinimumWidth(50)
@@ -263,7 +329,8 @@ class PreparePanel(QWidget, TaskManager):
self.table.setCellWidget(task_row, 3, lb_progress)
self.tasks[id_] = {'lb_status': lb_status,
self.tasks[id_] = {'bt_icon': bt_icon,
'lb_status': lb_status,
'lb_prog': lb_progress,
'progress': 0,
'lb_sub': lb_sub,
@@ -285,6 +352,22 @@ class PreparePanel(QWidget, TaskManager):
self._resize_columns()
def update_output(self, task_id: str, output: str):
full_output = self.output.get(task_id)
if full_output is None:
full_output = []
self.output[task_id] = full_output
task = self.tasks[task_id]
task['bt_icon'].setEnabled(True)
task['bt_icon'].setCursor(QCursor(Qt.PointingHandCursor))
task['bt_icon'].setToolTip(self.i18n['prepare.bt_icon.output'])
full_output.append(output)
if self.current_output_task == task_id:
self.textarea_output.appendPlainText(output)
def finish_task(self, task_id: str):
task = self.tasks[task_id]
task['lb_sub'].setText('')

View File

@@ -1,14 +1,16 @@
import os
from typing import Tuple
from PyQt5.QtWidgets import QInputDialog, QLineEdit
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor, QIcon
from PyQt5.QtWidgets import QInputDialog, QLineEdit, QDialogButtonBox
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.view import MessageType
from bauh.commons.system import new_subprocess
from bauh.view.core.config import read_config
from bauh.view.qt import css
from bauh.view.qt.dialog import show_message
from bauh.view.qt.view_utils import load_resource_icon
from bauh.view.util import util
from bauh.view.util.translation import I18n
@@ -25,7 +27,7 @@ def ask_root_password(context: ApplicationContext, i18n: I18n, app_config: dict
if store_password and context.root_password and validate_password(context.root_password):
return context.root_password, True
diag = QInputDialog()
diag = QInputDialog(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
diag.setStyleSheet("""QLineEdit { border-radius: 5px; font-size: 16px; border: 1px solid lightblue }""")
diag.setInputMode(QInputDialog.TextInput)
diag.setTextEchoMode(QLineEdit.Password)
@@ -34,6 +36,16 @@ def ask_root_password(context: ApplicationContext, i18n: I18n, app_config: dict
diag.setLabelText('')
diag.setOkButtonText(i18n['popup.root.continue'].capitalize())
diag.setCancelButtonText(i18n['popup.button.cancel'].capitalize())
bt_box = [c for c in diag.children() if isinstance(c, QDialogButtonBox)][0]
for bt in bt_box.buttons():
if bt_box.buttonRole(bt) == QDialogButtonBox.AcceptRole:
bt.setStyleSheet(css.OK_BUTTON)
bt.setCursor(QCursor(Qt.PointingHandCursor))
bt.setIcon(QIcon())
diag.resize(400, 200)
for attempt in range(3):

View File

@@ -334,6 +334,8 @@ popup.root.bad_password.title=Error
popup.root.continue=continuar
popup.root.title=Requireix la vostra contrasenya per a continuar
popup.screenshots.no_screenshot.body=no shan pogut recuperar captures de pantalla del {}
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=saltar
prepare_panel.title.finish=Llest
prepare_panel.title.start=Iniciando

View File

@@ -333,6 +333,8 @@ popup.root.bad_password.title=Fehler
popup.root.continue=Fortfahren
popup.root.title=Zum Fortfahren wird das Passwort benötigt.
popup.screenshots.no_screenshot.body={} Screenshots konnten nicht heruntergeladen werden.
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=skip
prepare_panel.title.finish=Bereit
prepare_panel.title.start=Initialisierung

View File

@@ -333,6 +333,8 @@ popup.root.bad_password.title=Error
popup.root.continue=continue
popup.root.title=Requires your password to continue
popup.screenshots.no_screenshot.body=it was not possible to retrieve {} screenshots
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=skip
prepare_panel.title.finish=Ready
prepare_panel.title.start=Initializing

View File

@@ -334,6 +334,8 @@ popup.root.bad_password.title=Error
popup.root.continue=continuar
popup.root.title=Proporcione su contraseña para continuar
popup.screenshots.no_screenshot.body=No fue posible recuperar las fotos de {}
prepare.bt_icon.no_output=No hay detalles del progreso de esta tarea para mostrar
prepare.bt_icon.output=Haga clic aquí para mostrar los detalles del progreso de esta tarea
prepare_panel.bt_skip.label=omitir
prepare_panel.title.finish=Listo
prepare_panel.title.start=Inicializando

View File

@@ -336,6 +336,8 @@ popup.root.continue=continuare
popup.root.title=Richiede la tua password per continuare
popup.screenshots.no_screenshot.body=non è stato possibile recuperare {} schermate
prepar_panel.bt_skip.label=saltare
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=skip
prepare_panel.title.finish=Pronto
prepare_panel.title.start=Inizializzazione

View File

@@ -333,6 +333,8 @@ popup.root.bad_password.title=Erro
popup.root.continue=prosseguir
popup.root.title=Requer sua senha para prosseguir
popup.screenshots.no_screenshot.body=Não foi possível obter as fotos de {}
prepare.bt_icon.no_output=Não existe detalhes do progresso dessa tarefa para ser exibido
prepare.bt_icon.output=Clique aqui para exibir detalhes do progresso dessa tarefa
prepare_panel.bt_skip.label=Pular
prepare_panel.title.finish=Pronto
prepare_panel.title.start=Iniciando

View File

@@ -333,6 +333,8 @@ popup.root.bad_password.title=Ошибка
popup.root.continue=Продолжить
popup.root.title=Требуется ваш пароль, чтобы продолжить
popup.screenshots.no_screenshot.body=не удалось получить скриншоты {}
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=Пропустить
prepare_panel.title.finish=Ready
prepare_panel.title.start=Initializing

View File

@@ -333,6 +333,8 @@ popup.root.bad_password.title=Hata
popup.root.continue=devam
popup.root.title=Devam etmek için şifre girmelisiniz
popup.screenshots.no_screenshot.body={} ekran görüntüsünü almak mümkün değildi
prepare.bt_icon.no_output=There are no progress details for this task to be displayed
prepare.bt_icon.output=Click here to display the progress details of this task
prepare_panel.bt_skip.label=atla
prepare_panel.title.finish=Hazır
prepare_panel.title.start=Başlatılıyor