Retrieving app information and history

This commit is contained in:
Vinícius Moreira
2019-06-26 15:52:26 -03:00
committed by GitHub
parent f9f47d1429
commit c2e59ba757
12 changed files with 694 additions and 69 deletions

View File

@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.3.0]
### Features
- Allows to retrieve app information and history by right clicking it.
- Allows app uninstall and downgrade by right clicking it.
### Improvements

View File

@@ -4,6 +4,8 @@ from typing import List
from fpakman.core import system
BASE_CMD = 'flatpak'
def app_str_to_json(line: str, version: str) -> dict:
@@ -39,22 +41,24 @@ def app_str_to_json(line: str, version: str) -> dict:
return app
def get_app_info_fields(app_id: str, branch: str, fields: List[str], check_runtime: bool = False):
def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_runtime: bool = False):
info = re.findall(r'\w+:\s.+', get_app_info(app_id, branch))
data = {}
fields_to_retrieve = len(fields) + (1 if check_runtime and 'ref' not in fields else 0)
for field in info:
if fields_to_retrieve == 0:
if fields and fields_to_retrieve == 0:
break
field_val = field.split(':')
field_name = field_val[0].lower()
if field_name in fields or (check_runtime and field_name == 'ref'):
if not fields or field_name in fields or (check_runtime and field_name == 'ref'):
data[field_name] = field_val[1].strip()
fields_to_retrieve -= 1
if fields:
fields_to_retrieve -= 1
if check_runtime and field_name == 'ref':
data['runtime'] = data['ref'].startswith('runtime/')
@@ -63,16 +67,16 @@ def get_app_info_fields(app_id: str, branch: str, fields: List[str], check_runti
def get_version():
res = system.run_cmd('flatpak --version')
res = system.run_cmd('{} --version'.format(BASE_CMD))
return res.split(' ')[1].strip() if res else None
def get_app_info(app_id: str, branch: str):
return system.run_cmd('flatpak info {} {}'.format(app_id, branch))
return system.run_cmd('{} info {} {}'.format(BASE_CMD, app_id, branch))
def list_installed() -> List[dict]:
apps_str = system.run_cmd('flatpak list')
apps_str = system.run_cmd('{} list'.format(BASE_CMD))
if apps_str:
version = get_version()
@@ -83,7 +87,7 @@ def list_installed() -> List[dict]:
def update(app_ref: str) -> bool:
return bool(system.run_cmd('flatpak update -y ' + app_ref))
return bool(system.run_cmd('{} update -y {}'.format(BASE_CMD, app_ref)))
def update_and_stream(app_ref: str):
@@ -92,7 +96,7 @@ def update_and_stream(app_ref: str):
:param app_ref:
:return:
"""
return system.stream_cmd(['flatpak', 'update', '-y', app_ref])
return system.stream_cmd([BASE_CMD, 'update', '-y', app_ref])
def uninstall_and_stream(app_ref: str):
@@ -101,11 +105,11 @@ def uninstall_and_stream(app_ref: str):
:param app_ref:
:return:
"""
return system.stream_cmd(['flatpak', 'uninstall', app_ref, '-y'])
return system.stream_cmd([BASE_CMD, 'uninstall', app_ref, '-y'])
def list_updates_as_str():
return system.run_cmd('flatpak update', ignore_return_code=True)
return system.run_cmd('{} update'.format(BASE_CMD), ignore_return_code=True)
def downgrade_and_stream(app_ref: str, commit: str, root_password: str):
@@ -114,12 +118,31 @@ def downgrade_and_stream(app_ref: str, commit: str, root_password: str):
if root_password is not None:
downgrade_cmd.extend(['sudo', '-S'])
pwdin = subprocess.Popen(['echo', root_password], stdout=subprocess.PIPE).stdout
pwdin = system.stream_cmd(['echo', root_password])
downgrade_cmd.extend(['flatpak', 'update', '--commit={}'.format(commit), app_ref, '-y'])
downgrade_cmd.extend([BASE_CMD, 'update', '--commit={}'.format(commit), app_ref, '-y'])
return subprocess.Popen(downgrade_cmd, stdin=pwdin, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout
def get_app_commits(app_ref: str, origin: str) -> List[str]:
log = system.run_cmd('flatpak remote-info --log {} {}'.format(origin, app_ref))
log = system.run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
return re.findall(r'Commit+:\s(.+)', log)
def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
log = system.run_cmd('{} remote-info --log {} {}'.format(BASE_CMD, origin, app_ref))
res = re.findall(r'(Commit|Subject|Date):\s(.+)', log)
commits = []
commit = {}
for idx, data in enumerate(res):
commit[data[0].strip().lower()] = data[1].strip()
if (idx + 1) % 3 == 0:
commits.append(commit)
commit = {}
return commits

View File

@@ -3,9 +3,9 @@ from typing import List
def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False) -> str:
res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, env={'LANG': 'en'})
return res.stdout.decode() if ignore_return_code or res.returncode == expected_code else None
def stream_cmd(cmd: List[str]):
return subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
return subprocess.Popen(cmd, stdout=subprocess.PIPE, env={'LANG': 'en'}).stdout

218
fpakman/resources/img/history.svg Executable file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

154
fpakman/resources/img/info.svg Executable file
View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="47.999996"
height="48.000004"
viewBox="0 0 47.999997 48.000005"
xml:space="preserve"
sodipodi:docname="info.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata128"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs126"><linearGradient
inkscape:collect="always"
id="linearGradient153"><stop
style="stop-color:#0000ff;stop-opacity:1;"
offset="0"
id="stop149" /><stop
style="stop-color:#0000ff;stop-opacity:0;"
offset="1"
id="stop151" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient153"
id="linearGradient155"
x1="-180.77992"
y1="-30.518711"
x2="337.13013"
y2="868.49902"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient153"
id="linearGradient157"
gradientUnits="userSpaceOnUse"
x1="-180.77992"
y1="-30.518711"
x2="337.13013"
y2="868.49902" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview124"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.8698707"
inkscape:cx="239.58875"
inkscape:cy="26.740828"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g93"
transform="translate(0,-390.534)">
</g>
<g
id="g95"
transform="translate(0,-390.534)">
</g>
<g
id="g97"
transform="translate(0,-390.534)">
</g>
<g
id="g99"
transform="translate(0,-390.534)">
</g>
<g
id="g101"
transform="translate(0,-390.534)">
</g>
<g
id="g103"
transform="translate(0,-390.534)">
</g>
<g
id="g105"
transform="translate(0,-390.534)">
</g>
<g
id="g107"
transform="translate(0,-390.534)">
</g>
<g
id="g109"
transform="translate(0,-390.534)">
</g>
<g
id="g111"
transform="translate(0,-390.534)">
</g>
<g
id="g113"
transform="translate(0,-390.534)">
</g>
<g
id="g115"
transform="translate(0,-390.534)">
</g>
<g
id="g117"
transform="translate(0,-390.534)">
</g>
<g
id="g119"
transform="translate(0,-390.534)">
</g>
<g
id="g121"
transform="translate(0,-390.534)">
</g>
<g
id="g96"><g
style="fill:url(#linearGradient155);fill-opacity:1"
transform="scale(0.10945609,0.1094556)"
id="g91">
<path
style="fill:url(#linearGradient157);fill-opacity:1"
inkscape:connector-curvature="0"
id="path89"
d="M 409.133,109.203 C 389.525,75.611 362.928,49.014 329.335,29.407 295.736,9.801 259.058,0 219.273,0 179.492,0 142.803,9.801 109.21,29.407 75.615,49.011 49.018,75.608 29.41,109.203 9.801,142.8 0,179.489 0,219.267 c 0,39.78 9.804,76.463 29.407,110.062 19.607,33.592 46.204,60.189 79.799,79.798 33.597,19.605 70.283,29.407 110.063,29.407 39.78,0 76.47,-9.802 110.065,-29.407 33.593,-19.602 60.189,-46.206 79.795,-79.798 19.603,-33.596 29.403,-70.284 29.403,-110.062 0.001,-39.782 -9.8,-76.472 -29.399,-110.064 z M 182.727,54.813 c 0,-2.666 0.855,-4.853 2.57,-6.565 1.712,-1.711 3.903,-2.57 6.567,-2.57 h 54.82 c 2.662,0 4.853,0.859 6.561,2.57 1.711,1.712 2.573,3.899 2.573,6.565 v 45.682 c 0,2.664 -0.862,4.854 -2.573,6.564 -1.708,1.712 -3.898,2.568 -6.561,2.568 h -54.82 c -2.664,0 -4.854,-0.856 -6.567,-2.568 -1.715,-1.709 -2.57,-3.9 -2.57,-6.564 z m 109.632,301.496 c 0,2.662 -0.863,4.853 -2.57,6.561 -1.704,1.714 -3.895,2.57 -6.563,2.57 H 155.317 c -2.667,0 -4.854,-0.856 -6.567,-2.57 -1.712,-1.708 -2.568,-3.898 -2.568,-6.564 v -45.682 c 0,-2.67 0.856,-4.853 2.568,-6.567 1.713,-1.708 3.903,-2.57 6.567,-2.57 h 27.41 v -91.358 h -27.41 c -2.667,0 -4.853,-0.855 -6.567,-2.568 -1.712,-1.711 -2.568,-3.901 -2.568,-6.567 v -45.679 c 0,-2.666 0.856,-4.853 2.568,-6.567 1.715,-1.713 3.905,-2.568 6.567,-2.568 h 91.367 c 2.662,0 4.853,0.855 6.561,2.568 1.711,1.714 2.573,3.901 2.573,6.567 v 146.179 h 27.401 c 2.669,0 4.859,0.855 6.57,2.566 1.704,1.712 2.566,3.901 2.566,6.567 v 45.683 h 0.004 z" />
</g><path
inkscape:connector-curvature="0"
id="path88"
d="m 16.36476,36.447391 v -3.208779 h 1.871787 1.871787 V 27.890649 22.542686 H 18.236547 16.36476 V 19.333908 16.12513 h 5.615361 5.615362 v 8.556741 8.556741 h 2.139185 2.139186 v 3.208779 3.208778 H 24.119307 16.36476 Z"
style="fill:#ffffff;stroke-width:0.53479636" /><path
inkscape:connector-curvature="0"
id="path90"
d="M 20.571824,11.490228 C 20.375732,11.294136 20.215294,9.8501861 20.215294,8.2814501 V 5.429203 h 3.743574 3.743574 v 3.208778 3.208778 h -3.387043 c -1.862874,0 -3.547483,-0.160439 -3.743575,-0.356531 z"
style="fill:#ffffff;stroke-width:0.53479636" /></g></svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,12 +1,8 @@
manage_window.title=Flatpak Application Manager
manage_window.columns.name=Name
manage_window.columns.version=Version
manage_window.columns.latest_version=Latest Version
manage_window.columns.branch=Branch
manage_window.columns.arch=Arch
manage_window.columns.ref=Ref
manage_window.columns.origin=Origin
manage_window.columns.update=Upgrade ?
manage_window.apps_table.row.actions.info=Information
manage_window.apps_table.row.actions.history=History
manage_window.apps_table.row.actions.uninstall=Uninstall
manage_window.apps_table.row.actions.uninstall.popup.title=Uninstall
manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} ?
@@ -18,6 +14,8 @@ manage_window.status.refreshing=Refreshing
manage_window.status.upgrading=Upgrading
manage_window.status.uninstalling=Uninstalling
manage_window.status.downgrading=Downgrading
manage_window.status.info=Retrieving information
manage_window.status.history=Retrieving history
manage_window.bt.refresh.tooltip=Reload the data about installed applications
manage_window.bt.upgrade.tooltip=Upgrade all selected applications
popup.flatpak_not_installed.title=Error
@@ -28,9 +26,29 @@ popup.root.bad_password.title=Error
popup.root.bad_password.body=Wrong password
popup.downgrade.impossible.title=Impossible to downgrade
popup.downgrade.impossible.body=The app is in its first version
popup.history.title=History
popup.history.selected.tooltip=Current version
popup.button.yes=Yes
popup.button.no=No
popup.button.cancel=Cancel
tray.action.manage=Manage applications
tray.action.exit=Exit
notification.new_updates=Updates found
notification.new_updates=Updates found
flatpak.info.arch=arch
flatpak.info.branch=branch
flatpak.info.collection=collection
flatpak.info.commit=commit
flatpak.info.date=date
flatpak.info.id=id
flatpak.info.installation=installation
flatpak.info.installed=installed
flatpak.info.license=license
flatpak.info.name=name
flatpak.info.origin=origin
flatpak.info.parent=parent
flatpak.info.ref=ref
flatpak.info.runtime=runtime
flatpak.info.sdk=sdk
flatpak.info.subject=subject
flatpak.info.type=type
flatpak.info.version=version

View File

@@ -1,23 +1,21 @@
manage_window.title=Administrador de Aplicativos Flatpak
manage_window.columns.name=Nombre
manage_window.columns.version=Versión
manage_window.columns.latest_version=Ultima Versión
manage_window.columns.branch=Rama
manage_window.columns.arch=Arch
manage_window.columns.ref=Ref
manage_window.columns.origin=Origen
manage_window.columns.update=¿Actualizar?
manage_window.columns.update=Actualizar ?
manage_window.apps_table.row.actions.info=Información
manage_window.apps_table.row.actions.history=Historia
manage_window.apps_table.row.actions.uninstall=Desinstalar
manage_window.apps_table.row.actions.uninstall.popup.title=Desinstalación
manage_window.apps_table.row.actions.uninstall.popup.body=¿Eliminar {}?
manage_window.apps_table.row.actions.downgrade=Revertir versión
manage_window.apps_table.row.actions.downgrade.popup.body=¿Realmente quieres revertir la versión actual de {}?
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.only_apps=Aplicativos
manage_window.label.updates=Actualizaciones
manage_window.status.refreshing=Recargando
manage_window.status.upgrading=Actualizando
manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revirtiendo
manage_window.status.info=Obteniendo información
manage_window.status.history=Obteniendo la historia
manage_window.bt.refresh.tooltip=Recarga los datos sobre los aplicativos instalados
manage_window.bt.upgrade.tooltip=Actualiza todos los aplicativos seleccionados
popup.flatpak_not_installed.title=Error
@@ -28,9 +26,29 @@ popup.root.bad_password.title=Error
popup.root.bad_password.body=Contraseña incorrecta
popup.downgrade.impossible.title=Imposible revertir la versión
popup.downgrade.impossible.body=El aplicativo está en su primera versión
popup.history.title=Historia
popup.history.selected.tooltip=Versión actual
popup.button.yes=Sí
popup.button.no=No
popup.button.cancel=Cancelar
tray.action.manage=Administrar aplicativos
tray.action.exit=Salir
notification.new_updates=Actualizaciones encontradas
notification.new_updates=Actualizaciones encontradas
flatpak.info.arch=arquitectura
flatpak.info.branch=rama
flatpak.info.collection=colección
flatpak.info.commit=commit
flatpak.info.date=fecha
flatpak.info.id=id
flatpak.info.installation=instalación
flatpak.info.installed=instalado
flatpak.info.license=licencia
flatpak.info.name=nombre
flatpak.info.origin=origen
flatpak.info.parent=padre
flatpak.info.ref=ref
flatpak.info.runtime=runtime
flatpak.info.sdk=sdk
flatpak.info.subject=tema
flatpak.info.type=tipo
flatpak.info.version=versión

View File

@@ -1,23 +1,21 @@
manage_window.title=Gerenciador de Aplicativos Flatpak
manage_window.columns.name=Nome
manage_window.columns.version=Versão
manage_window.columns.latest_version=Última Versão
manage_window.columns.branch=Ramo
manage_window.columns.arch=Arch
manage_window.columns.ref=Ref
manage_window.columns.origin=Origem
manage_window.columns.update=Atualizar ?
manage_window.apps_table.row.actions.info=Informação
manage_window.apps_table.row.actions.history=Histórico
manage_window.apps_table.row.actions.uninstall=Desinstalar
manage_window.apps_table.row.actions.uninstall.popup.title=Desinstalação
manage_window.apps_table.row.actions.uninstall.popup.body=Remover {}?
manage_window.apps_table.row.actions.downgrade=Reverter versão
manage_window.apps_table.row.actions.downgrade.popup.body=Você realmente quer reverter a versão atual de {} ?
manage_window.checkbox.only_apps=Apps
manage_window.checkbox.only_apps=Aplicativos
manage_window.label.updates=Atualizações
manage_window.status.refreshing=Recarregando
manage_window.status.upgrading=Atualizando
manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revertendo
manage_window.status.info=Obtendo informação
manage_window.status.history=Obtendo histórico
manage_window.bt.refresh.tooltip=Recarrega os dados sobre os aplicativos instalados
manage_window.bt.upgrade.tooltip=Atualiza todos os aplicativos selecionados
popup.flatpak_not_installed.title=Erro
@@ -28,9 +26,29 @@ popup.root.bad_password.title=Erro
popup.root.bad_password.body=Senha incorreta
popup.downgrade.impossible.title=Impossível reverter a versão
popup.downgrade.impossible.body=O aplicativo está na sua primeira versão
popup.history.title=Histórico
popup.history.selected.tooltip=Versão atual
popup.button.yes=Sim
popup.button.no=Não
popup.button.cancel=Cancelar
tray.action.manage=Gerenciar aplicativos
tray.action.exit=Sair
notification.new_updates=Atualizações encontradas
notification.new_updates=Atualizações encontradas
flatpak.info.arch=arquitetura
flatpak.info.branch=ramo
flatpak.info.collection=coleção
flatpak.info.commit=commit
flatpak.info.date=data
flatpak.info.id=id
flatpak.info.installation=instalação
flatpak.info.installed=instalado
flatpak.info.license=licença
flatpak.info.name=nome
flatpak.info.origin=origem
flatpak.info.parent=pai
flatpak.info.ref=ref
flatpak.info.runtime=runtime
flatpak.info.sdk=sdk
flatpak.info.subject=assunto
flatpak.info.type=tipo
flatpak.info.version=versão

View File

@@ -1,12 +1,13 @@
import json
from typing import List
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtCore import Qt, QUrl, QThread, pyqtSignal
from PyQt5.QtGui import QPixmap, QIcon, QColor, QCursor
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QMessageBox, QHeaderView
QMessageBox, QHeaderView, QDialog, QLineEdit, QVBoxLayout, QPlainTextEdit
from fpakman.core import resource
from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakController
from fpakman.view.qt import dialog
@@ -48,8 +49,6 @@ class AppsTable(QTableWidget):
self.setHorizontalHeaderLabels(columns)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.icon_flathub = QIcon(resource.get_path('img/flathub_45.svg'))
self.icon_uninstall = QIcon(resource.get_path('img/uninstall.svg'))
self.icon_downgrade = QIcon(resource.get_path('img/downgrade.svg'))
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon)
@@ -59,28 +58,41 @@ class AppsTable(QTableWidget):
def contextMenuEvent(self, QContextMenuEvent): # selected row right click event
menu_row = QMenu()
action_info = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.info"])
action_info.setIcon(QIcon(resource.get_path('img/info.svg')))
action_info.triggered.connect(self._get_app_info)
menu_row.addAction(action_info)
action_history = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.history"])
action_history.setIcon(QIcon(resource.get_path('img/history.svg')))
action_history.triggered.connect(self._get_app_history)
menu_row.addAction(action_history)
action_uninstall = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.uninstall"])
action_uninstall.setIcon(self.icon_uninstall)
action_uninstall.setIcon(QIcon(resource.get_path('img/uninstall.svg')))
action_uninstall.triggered.connect(self._uninstall_app)
menu_row.addAction(action_uninstall)
app = self._get_selected_app()
app = self.get_selected_app()
if not app['model']['runtime']: # downgrade only allowed for apps
action_downgrade = QAction(self.parent.locale_keys["manage_window.apps_table.row.actions.downgrade"])
action_downgrade.triggered.connect(self._downgrade_app)
action_downgrade.setIcon(self.icon_downgrade)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
menu_row.addAction(action_downgrade)
menu_row.adjustSize()
menu_row.popup(QCursor.pos())
menu_row.exec_()
def _get_selected_app(self):
def get_selected_app(self):
return self.parent.apps[self.currentRow()]
def get_selected_app_icon(self):
return self.item(self.currentRow(), 0).icon()
def _uninstall_app(self):
selected_app = self._get_selected_app()
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
body=self.parent.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(selected_app['model']['name']),
@@ -88,13 +100,19 @@ class AppsTable(QTableWidget):
self.parent.uninstall_app(selected_app['model']['ref'])
def _downgrade_app(self):
selected_app = self._get_selected_app()
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.parent.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app['model']['name']),
locale_keys=self.parent.locale_keys):
self.parent.downgrade_app(selected_app['model']['ref'])
def _get_app_info(self):
self.parent.get_app_info(self.get_selected_app())
def _get_app_history(self):
self.parent.get_app_history(self.get_selected_app())
def _load_icon(self, http_response):
icon_url = http_response.url().toString()
pixmap = QPixmap()

View File

@@ -0,0 +1,52 @@
import operator
from functools import reduce
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
class HistoryDialog(QDialog):
def __init__(self, app: dict, app_icon: QIcon, locale_keys: dict):
super(HistoryDialog, self).__init__()
self.setWindowTitle('{} - {} '.format(locale_keys['popup.history.title'], app['model']['name']))
self.setWindowIcon(app_icon)
layout = QVBoxLayout()
self.setLayout(layout)
table_history = QTableWidget()
table_history.setFocusPolicy(Qt.NoFocus)
table_history.setShowGrid(False)
table_history.verticalHeader().setVisible(False)
table_history.setAlternatingRowColors(True)
table_history.setColumnCount(len(app['commits'][0]))
table_history.setRowCount(len(app['commits']))
table_history.setHorizontalHeaderLabels([locale_keys['flatpak.info.' + key].capitalize() for key in sorted(app['commits'][0].keys())])
for row, commit in enumerate(app['commits']):
current_app_commit = app['model']['commit'] == commit['commit']
for col, key in enumerate(sorted(commit.keys())):
item = QTableWidgetItem()
item.setText(commit[key])
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if current_app_commit:
item.setBackground(Qt.darkYellow)
item.setToolTip(locale_keys['popup.history.selected.tooltip'])
table_history.setItem(row, col, item)
layout.addWidget(table_history)
header_horizontal = table_history.horizontalHeader()
for i in range(0, table_history.columnCount()):
header_horizontal.setSectionResizeMode(i, QHeaderView.Stretch)
new_width = reduce(operator.add, [table_history.columnWidth(i) for i in range(table_history.columnCount())])
self.resize(new_width, table_history.height())

34
fpakman/view/qt/info.py Normal file
View File

@@ -0,0 +1,34 @@
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QFormLayout, QGroupBox, \
QLineEdit, QLabel
class InfoDialog(QDialog):
def __init__(self, app: dict, app_icon: QIcon, locale_keys: dict):
super(InfoDialog, self).__init__()
self.setWindowTitle(app['name'])
self.setWindowIcon(app_icon)
layout = QVBoxLayout()
self.setLayout(layout)
gbox_info_layout = QFormLayout()
gbox_info = QGroupBox()
gbox_info.setLayout(gbox_info_layout)
layout.addWidget(gbox_info)
for attr in sorted(app.keys()):
if attr != 'name':
text = QLineEdit()
text.setText(app[attr])
text.setStyleSheet("width: 400px")
text.setReadOnly(True)
text.setCursorPosition(0)
label = QLabel("{}: ".format(locale_keys.get('flatpak.info.' + attr, attr)).capitalize())
label.setStyleSheet("font-weight: bold")
gbox_info_layout.addRow(label, text)
self.adjustSize()

View File

@@ -1,19 +1,22 @@
import json
import operator
from functools import reduce
from threading import Lock
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal, QEvent
from PyQt5.QtCore import QThread, pyqtSignal, QEvent, Qt
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QMessageBox, QPlainTextEdit
from fpakman.core.model import FlatpakManager, ImpossibleDowngradeException
QSizePolicy, QLabel, QPlainTextEdit, QDialog, QGroupBox, QFormLayout, QLineEdit, QTableWidget, QTableWidgetItem
from fpakman import __version__
from fpakman.core import resource
from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakController
from fpakman.core.model import FlatpakManager, ImpossibleDowngradeException
from fpakman.view.qt import dialog
from fpakman.view.qt.apps_table import AppsTable
from fpakman.view.qt.history import HistoryDialog
from fpakman.view.qt.info import InfoDialog
from fpakman.view.qt.root import is_root, ask_root_password
@@ -21,17 +24,17 @@ class ManageWindow(QWidget):
__BASE_HEIGHT__ = 400
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, tray_icon = None):
def __init__(self, locale_keys: dict, controller: FlatpakController, manager: FlatpakManager, tray_icon=None):
super(ManageWindow, self).__init__()
self.locale_keys = locale_keys
self.column_names = [locale_keys['manage_window.columns.name'],
locale_keys['manage_window.columns.version'],
locale_keys['manage_window.columns.latest_version'],
locale_keys['manage_window.columns.branch'],
locale_keys['manage_window.columns.arch'],
locale_keys['manage_window.columns.ref'],
locale_keys['manage_window.columns.origin'],
locale_keys['manage_window.columns.update']]
self.column_names = [locale_keys[key].capitalize() for key in ['flatpak.info.name',
'flatpak.info.version',
'manage_window.columns.latest_version',
'flatpak.info.branch',
'flatpak.info.arch',
'flatpak.info.ref',
'flatpak.info.origin',
'manage_window.columns.update']]
self.controller = controller
self.manager = manager
self.tray_icon = tray_icon
@@ -112,6 +115,12 @@ class ManageWindow(QWidget):
self.thread_downgrade.signal_output.connect(self._update_action_output)
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
self.thread_get_info = GetAppInfo()
self.thread_get_info.signal_finished.connect(self._finish_get_info)
self.thread_get_history = GetAppHistory()
self.thread_get_history.signal_finished.connect(self._finish_get_history)
self.toolbar_bottom = QToolBar()
self.label_updates = QLabel('')
self.toolbar_bottom.addWidget(self.label_updates)
@@ -174,7 +183,7 @@ class ManageWindow(QWidget):
if self._acquire_lock():
self._check_flatpak_installed()
self._begin_action(self.locale_keys['manage_window.status.refreshing'] + '...')
self._begin_action(self.locale_keys['manage_window.status.refreshing'])
if clear_output:
self.textarea_output.clear()
@@ -194,7 +203,7 @@ class ManageWindow(QWidget):
if self._acquire_lock():
self.textarea_output.clear()
self.textarea_output.setVisible(True)
self._begin_action(self.locale_keys['manage_window.status.uninstalling'] + '...')
self._begin_action(self.locale_keys['manage_window.status.uninstalling'])
self.thread_uninstall.app_ref = app_ref
self.thread_uninstall.start()
@@ -303,7 +312,7 @@ class ManageWindow(QWidget):
self.textarea_output.clear()
self.textarea_output.setVisible(True)
self._begin_action(self.locale_keys['manage_window.status.upgrading'] + '...')
self._begin_action(self.locale_keys['manage_window.status.upgrading'])
self.thread_update.refs_to_update = to_update
self.thread_update.start()
@@ -316,7 +325,7 @@ class ManageWindow(QWidget):
self.textarea_output.appendPlainText(output)
def _begin_action(self, action_label: str):
self.label_status.setText(action_label)
self.label_status.setText(action_label + "...")
self.bt_upgrade.setEnabled(False)
self.bt_refresh.setEnabled(False)
self.checkbox_only_apps.setEnabled(False)
@@ -345,12 +354,44 @@ class ManageWindow(QWidget):
self.textarea_output.clear()
self.textarea_output.setVisible(True)
self._begin_action(self.locale_keys['manage_window.status.downgrading'] + '...')
self._begin_action(self.locale_keys['manage_window.status.downgrading'])
self.thread_downgrade.app_ref = app_ref
self.thread_downgrade.root_password = pwd
self.thread_downgrade.start()
def get_app_info(self, app: dict):
if self._acquire_lock():
self.textarea_output.clear()
self.textarea_output.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.info'])
self.thread_get_info.app = app
self.thread_get_info.start()
def get_app_history(self, app: dict):
if self._acquire_lock():
self.textarea_output.clear()
self.textarea_output.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.history'])
self.thread_get_history.app = app
self.thread_get_history.start()
def _finish_get_info(self, app_info: dict):
self._release_lock()
self.finish_action()
dialog_info = InfoDialog(app_info, self.table_apps.get_selected_app_icon(), self.locale_keys)
dialog_info.exec_()
def _finish_get_history(self, app: dict):
self._release_lock()
self.finish_action()
dialog_history = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys)
dialog_history.exec_()
# Threaded actions
class UpdateSelectedApps(QThread):
@@ -433,3 +474,33 @@ class DowngradeApp(QThread):
self.app_ref = None
self.root_password = None
self.signal_finished.emit()
class GetAppInfo(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppInfo, self).__init__()
self.app = None
def run(self):
if self.app:
app_info = flatpak.get_app_info_fields(self.app['model']['id'], self.app['model']['branch'])
app_info['name'] = self.app['model']['name']
app_info['type'] = 'runtime' if self.app['model']['runtime'] else 'app'
self.signal_finished.emit(app_info)
self.app = None
class GetAppHistory(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self):
super(GetAppHistory, self).__init__()
self.app = None
def run(self):
if self.app:
commits = flatpak.get_app_commits_data(self.app['model']['ref'], self.app['model']['origin'])
self.signal_finished.emit({'model': self.app['model'], 'commits': commits})
self.app = None