search and install

This commit is contained in:
Vinícius Moreira
2019-06-28 19:01:37 -03:00
committed by GitHub
parent e3a8df5dab
commit 22eed7af67
16 changed files with 649 additions and 83 deletions

View File

@@ -6,8 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.3.0] - 2019-06-XX ## [0.3.0] - 2019-06-XX
### Features ### Features
- Allows to retrieve app information and history by right clicking it. - Applications search
- Allows app uninstall and downgrade by right clicking it. - Now when you right-click a selected application you can:
- retrieve its information
- retrieve its commit history
- downgrade
- install it
- "About" window available when right-clicking the tray icon. - "About" window available when right-clicking the tray icon.
### Improvements ### Improvements
@@ -18,7 +22,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Showing runtime apps when no app is available - Showing runtime apps when no app is available
- Allowing to specify a custom app translation with the environment variable **FPAKMAN_LOCALE** - Allowing to specify a custom app translation with the environment variable **FPAKMAN_LOCALE**
- Adding expiration time for cached app data. Default to 1 hour. The environment variable **FPAKMAN_CACHE_EXPIRATION** can change this value. - Adding expiration time for cached app data. Default to 1 hour. The environment variable **FPAKMAN_CACHE_EXPIRATION** can change this value.
- Retrieving only the installed applications data from the origin API.
- Minor GUI improvements - Minor GUI improvements
## [0.2.1] - 2019-06-24 ## [0.2.1] - 2019-06-24

View File

@@ -25,6 +25,7 @@ manager = FlatpakManager(cache_expire=int(os.getenv('FPAKMAN_CACHE_EXPIRATION',
trayIcon = TrayIcon(locale_keys=locale_keys, trayIcon = TrayIcon(locale_keys=locale_keys,
manager=manager, manager=manager,
check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60))) check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60)))
trayIcon.load_database()
trayIcon.show() trayIcon.show()
sys.exit(app.exec_()) sys.exit(app.exec_())

View File

@@ -1,5 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from threading import Lock, Thread from threading import Lock
from typing import List from typing import List
import requests import requests
@@ -19,17 +19,12 @@ class FlatpakManager:
self.lock_db_read = Lock() self.lock_db_read = Lock()
self.lock_read = Lock() self.lock_read = Lock()
# TODO remove if not necessary for future releases
def load_full_database_async(self):
Thread(target=self.load_full_database, daemon=True).start()
# TODO remove if not necessary for future releases
def load_full_database(self): def load_full_database(self):
self.lock_db_read.acquire() self.lock_db_read.acquire()
try: try:
res = self.http_session.get(__FLATHUB_API_URL__ + '/apps') res = self.http_session.get(__FLATHUB_API_URL__ + '/apps', timeout=30)
if res.status_code == 200: if res.status_code == 200:
for app in res.json(): for app in res.json():
@@ -40,7 +35,7 @@ class FlatpakManager:
def _request_app_data(self, app_id: str): def _request_app_data(self, app_id: str):
try: try:
res = self.http_session.get('{}/apps/{}'.format(__FLATHUB_API_URL__, app_id), timeout=60) res = self.http_session.get('{}/apps/{}'.format(__FLATHUB_API_URL__, app_id), timeout=30)
if res.status_code == 200: if res.status_code == 200:
return res.json() return res.json()
@@ -50,6 +45,58 @@ class FlatpakManager:
print("Could not retrieve app data for id '{}'. Timeout".format(app_id)) print("Could not retrieve app data for id '{}'. Timeout".format(app_id))
return None return None
def _fill_api_data(self, app: dict):
api_data = self.cache_apps.get(app['id'])
if (not app['runtime'] and not api_data) or (api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.utcnow()): # if api data is not cached or expired, tries to retrieve it
api_data = self._request_app_data(app['id'])
if api_data:
api_data['expires_at'] = datetime.utcnow() + timedelta(seconds=self.cache_expire)
self.cache_apps[app['id']] = api_data
if not api_data:
for attr in ('latest_version', 'icon', 'description'):
if attr not in app:
app[attr] = None
else:
app['latest_version'] = api_data.get('currentReleaseVersion')
app['icon'] = api_data.get('iconMobileUrl')
for attr in ('name', 'description'):
if not app.get(attr):
app[attr] = api_data.get(attr)
if app['icon'].startswith('/'):
app['icon'] = __FLATHUB_URL__ + app['icon']
def search(self, word: str) -> List[dict]:
res = []
apps_found = flatpak.search(word)
if apps_found:
already_read = set()
installed_apps = self.read_installed()
if installed_apps:
for app in apps_found:
for installed_app in installed_apps:
if app['id'] == installed_app['id']:
res.append(installed_app)
already_read.add(app['id'])
for app in apps_found:
if app['id'] not in already_read:
app['update'] = False
app['installed'] = False
self._fill_api_data(app)
res.append(app)
return res
def read_installed(self) -> List[dict]: def read_installed(self) -> List[dict]:
self.lock_read.acquire() self.lock_read.acquire()
@@ -63,25 +110,9 @@ class FlatpakManager:
available_updates = flatpak.list_updates_as_str() available_updates = flatpak.list_updates_as_str()
for app in installed: for app in installed:
self._fill_api_data(app)
app_data = self.cache_apps.get(app['id'])
if (not app['runtime'] and not app_data) or (app_data and app_data['expires_at'] <= datetime.utcnow()): # if data is not cached or expired, tries to retrieve it
app_data = self._request_app_data(app['id'])
app_data['expires_at'] = datetime.utcnow() + timedelta(seconds=self.cache_expire)
self.cache_apps[app['id']] = app_data
if not app_data:
app['latest_version'] = None
app['icon'] = None
else:
app['latest_version'] = app_data['currentReleaseVersion']
app['icon'] = app_data['iconMobileUrl']
if app['icon'].startswith('/'):
app['icon'] = __FLATHUB_URL__ + app['icon']
app['update'] = app['id'] in available_updates app['update'] = app['id'] in available_updates
app['installed'] = True
return installed return installed

View File

@@ -147,3 +147,67 @@ def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
commit = {} commit = {}
return commits return commits
def search(word: str) -> List[dict]:
cli_version = get_version()
res = system.run_cmd('{} search {}'.format(BASE_CMD, word))
found = []
split_res = res.split('\n')
if split_res and split_res[0].lower() != 'no matches found':
for info in split_res:
if info:
info_list = info.split('\t')
if cli_version >= '1.3.0':
version = info_list[3].strip()
found.append({
'name': info_list[0].strip(),
'description': info_list[1].strip(),
'id': info_list[2].strip(),
'version': version,
'latest_version': version,
'branch': info_list[4].strip(),
'origin': info_list[5].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
})
elif cli_version >= '1.2.0':
desc = info_list[0].split('-')
version = info_list[2].strip()
found.append({
'name': desc[0].strip(),
'description': desc[1].strip(),
'id': info_list[1].strip(),
'version': version,
'latest_version': version,
'branch': info_list[3].strip(),
'origin': info_list[4].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
})
else:
version = info_list[1].strip()
found.append({
'name': '',
'description': info_list[4].strip(),
'id': info_list[0].strip(),
'version': version,
'latest_version': version,
'branch': info_list[2].strip(),
'origin': info_list[3].strip(),
'runtime': False,
'arch': None, # unknown at this moment,
'ref': None # unknown at this moment
})
return found
def install_and_stream(app_id: str, origin: str):
return system.stream_cmd([BASE_CMD, 'install', origin, app_id, '-y'])

View File

@@ -1,6 +1,9 @@
import os
import subprocess import subprocess
from typing import List from typing import List
from fpakman.core import resource
def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False) -> str: def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False) -> str:
res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, env={'LANG': 'en'}) res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, env={'LANG': 'en'})
@@ -9,3 +12,8 @@ def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False)
def stream_cmd(cmd: List[str]): def stream_cmd(cmd: List[str]):
return subprocess.Popen(cmd, stdout=subprocess.PIPE, env={'LANG': 'en'}).stdout return subprocess.Popen(cmd, stdout=subprocess.PIPE, env={'LANG': 'en'}).stdout
def notify_user(msg: str, icon_path: str = resource.get_path('img/flathub_45.svg')):
if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)):
os.system("notify-send {} '{}'".format("-i {}".format(icon_path) if icon_path else '', msg))

View File

@@ -2,6 +2,9 @@ import locale
from fpakman.core import resource from fpakman.core import resource
import glob import glob
import re
HTML_RE = re.compile(r'<[^>]+>')
def get_locale_keys(key: str = None): def get_locale_keys(key: str = None):
@@ -38,3 +41,7 @@ def get_locale_keys(key: str = None):
locale_obj[keyval[0].strip()] = keyval[1].strip() locale_obj[keyval[0].strip()] = keyval[1].strip()
return locale_obj return locale_obj
def strip_html(string: str):
return HTML_RE.sub('', string)

120
fpakman/resources/img/install.svg Executable file
View File

@@ -0,0 +1,120 @@
<?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: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="48"
height="48"
viewBox="0 0 48 47.999999"
xml:space="preserve"
sodipodi:docname="install.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata135"><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="defs133" /><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="namedview131"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.67615769"
inkscape:cx="286.91482"
inkscape:cy="174.51451"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g98"
transform="scale(0.137524)"
style="fill:#00ff00">
<path
d="m 349.03,141.226 v 66.579 c 0,5.012 -4.061,9.079 -9.079,9.079 H 216.884 v 123.067 c 0,5.019 -4.067,9.079 -9.079,9.079 h -66.579 c -5.009,0 -9.079,-4.061 -9.079,-9.079 V 216.884 H 9.079 C 4.063,216.884 0,212.817 0,207.805 v -66.579 c 0,-5.013 4.063,-9.079 9.079,-9.079 H 132.147 V 9.079 C 132.147,4.061 136.216,0 141.226,0 h 66.579 c 5.012,0 9.079,4.061 9.079,9.079 v 123.068 h 123.067 c 5.019,0 9.079,4.066 9.079,9.079 z"
id="path96"
inkscape:connector-curvature="0"
style="fill:#00ff00" />
</g>
<g
id="g100"
transform="translate(0,-301.03)">
</g>
<g
id="g102"
transform="translate(0,-301.03)">
</g>
<g
id="g104"
transform="translate(0,-301.03)">
</g>
<g
id="g106"
transform="translate(0,-301.03)">
</g>
<g
id="g108"
transform="translate(0,-301.03)">
</g>
<g
id="g110"
transform="translate(0,-301.03)">
</g>
<g
id="g112"
transform="translate(0,-301.03)">
</g>
<g
id="g114"
transform="translate(0,-301.03)">
</g>
<g
id="g116"
transform="translate(0,-301.03)">
</g>
<g
id="g118"
transform="translate(0,-301.03)">
</g>
<g
id="g120"
transform="translate(0,-301.03)">
</g>
<g
id="g122"
transform="translate(0,-301.03)">
</g>
<g
id="g124"
transform="translate(0,-301.03)">
</g>
<g
id="g126"
transform="translate(0,-301.03)">
</g>
<g
id="g128"
transform="translate(0,-301.03)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

120
fpakman/resources/img/search.svg Executable file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.1, 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: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"
viewBox="0 0 12 12"
xml:space="preserve"
sodipodi:docname="search.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
width="12"
height="12"><metadata
id="metadata126"><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="defs124" /><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="namedview122"
showgrid="false"
showborder="false"
inkscape:zoom="0.53133476"
inkscape:cx="834.65593"
inkscape:cy="-101.88082"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="Search"
transform="scale(0.04794017)"
style="fill:#999999">
<path
style="clip-rule:evenodd;fill:#999999;fill-rule:evenodd"
d="m 244.186,214.604 -54.379,-54.378 c -0.289,-0.289 -0.628,-0.491 -0.93,-0.76 10.7,-16.231 16.945,-35.66 16.945,-56.554 C 205.822,46.075 159.747,0 102.911,0 46.075,0 0,46.075 0,102.911 c 0,56.835 46.074,102.911 102.91,102.911 20.895,0 40.323,-6.245 56.554,-16.945 0.269,0.301 0.47,0.64 0.759,0.929 l 54.38,54.38 c 8.169,8.168 21.413,8.168 29.583,0 8.168,-8.169 8.168,-21.413 0,-29.582 z M 102.911,170.146 c -37.134,0 -67.236,-30.102 -67.236,-67.235 0,-37.134 30.103,-67.236 67.236,-67.236 37.132,0 67.235,30.103 67.235,67.236 0,37.133 -30.103,67.235 -67.235,67.235 z"
id="path88"
inkscape:connector-curvature="0" />
</g>
<g
id="g91"
transform="translate(0,-238.312)">
</g>
<g
id="g93"
transform="translate(0,-238.312)">
</g>
<g
id="g95"
transform="translate(0,-238.312)">
</g>
<g
id="g97"
transform="translate(0,-238.312)">
</g>
<g
id="g99"
transform="translate(0,-238.312)">
</g>
<g
id="g101"
transform="translate(0,-238.312)">
</g>
<g
id="g103"
transform="translate(0,-238.312)">
</g>
<g
id="g105"
transform="translate(0,-238.312)">
</g>
<g
id="g107"
transform="translate(0,-238.312)">
</g>
<g
id="g109"
transform="translate(0,-238.312)">
</g>
<g
id="g111"
transform="translate(0,-238.312)">
</g>
<g
id="g113"
transform="translate(0,-238.312)">
</g>
<g
id="g115"
transform="translate(0,-238.312)">
</g>
<g
id="g117"
transform="translate(0,-238.312)">
</g>
<g
id="g119"
transform="translate(0,-238.312)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1,5 +1,6 @@
manage_window.title=Flatpak Application Manager manage_window.title=Flatpak Application Manager
manage_window.columns.latest_version=Latest Version manage_window.columns.latest_version=Latest Version
manage_window.columns.installed=Installed
manage_window.columns.update=Upgrade ? manage_window.columns.update=Upgrade ?
manage_window.apps_table.row.actions.info=Information manage_window.apps_table.row.actions.info=Information
manage_window.apps_table.row.actions.history=History manage_window.apps_table.row.actions.history=History
@@ -8,8 +9,11 @@ manage_window.apps_table.row.actions.uninstall.popup.title=Uninstall
manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} ? manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} ?
manage_window.apps_table.row.actions.downgrade=Downgrade manage_window.apps_table.row.actions.downgrade=Downgrade
manage_window.apps_table.row.actions.downgrade.popup.body=Do you really want to downgrade {} ? manage_window.apps_table.row.actions.downgrade.popup.body=Do you really want to downgrade {} ?
manage_window.apps_table.row.actions.install=Install
manage_window.apps_table.upgrade_toggle.tooltip=Click it to check or uncheck the update manage_window.apps_table.upgrade_toggle.tooltip=Click it to check or uncheck the update
manage_window.checkbox.only_apps=Apps manage_window.checkbox.only_apps=Apps
window_manage.input_search.placeholder=Search
window_manage.input_search.tooltip=Type and press ENTER to search for applications
manage_window.label.updates=Updates manage_window.label.updates=Updates
manage_window.status.refreshing=Refreshing manage_window.status.refreshing=Refreshing
manage_window.status.upgrading=Upgrading manage_window.status.upgrading=Upgrading
@@ -17,6 +21,8 @@ manage_window.status.uninstalling=Uninstalling
manage_window.status.downgrading=Downgrading manage_window.status.downgrading=Downgrading
manage_window.status.info=Retrieving information manage_window.status.info=Retrieving information
manage_window.status.history=Retrieving history manage_window.status.history=Retrieving history
manage_window.status.searching=Searching
manage_window.status.installing=Installing
manage_window.bt.refresh.tooltip=Reload the data about installed applications manage_window.bt.refresh.tooltip=Reload the data about installed applications
manage_window.bt.upgrade.tooltip=Upgrade all selected applications manage_window.bt.upgrade.tooltip=Upgrade all selected applications
popup.flatpak_not_installed.title=Error popup.flatpak_not_installed.title=Error
@@ -35,12 +41,14 @@ popup.button.cancel=Cancel
tray.action.manage=Manage applications tray.action.manage=Manage applications
tray.action.exit=Exit tray.action.exit=Exit
tray.action.about=About tray.action.about=About
tray.action.refreshing=Refreshing
notification.new_updates=Updates found notification.new_updates=Updates found
flatpak.info.arch=arch flatpak.info.arch=arch
flatpak.info.branch=branch flatpak.info.branch=branch
flatpak.info.collection=collection flatpak.info.collection=collection
flatpak.info.commit=commit flatpak.info.commit=commit
flatpak.info.date=date flatpak.info.date=date
flatpak.info.description=description
flatpak.info.id=id flatpak.info.id=id
flatpak.info.installation=installation flatpak.info.installation=installation
flatpak.info.installed=installed flatpak.info.installed=installed
@@ -57,4 +65,6 @@ flatpak.info.version=version
about.info.desc=Non-official Flatpak application management graphical interface about.info.desc=Non-official Flatpak application management graphical interface
about.info.link=More information at about.info.link=More information at
about.info.license=Free license about.info.license=Free license
about.info.rate=If this tool is useful for you, give it a star on Github to keep it up about.info.rate=If this tool is useful for you, give it a star on Github to keep it up
yes=yes
no=no

View File

@@ -1,6 +1,7 @@
manage_window.title=Administrador de Aplicativos Flatpak manage_window.title=Administrador de Aplicativos Flatpak
manage_window.columns.latest_version=Ultima Versión manage_window.columns.latest_version=Ultima Versión
manage_window.columns.update=Actualizar ? manage_window.columns.update=Actualizar ?
manage_window.columns.installed=Instalado
manage_window.apps_table.row.actions.info=Información manage_window.apps_table.row.actions.info=Información
manage_window.apps_table.row.actions.history=Historia manage_window.apps_table.row.actions.history=Historia
manage_window.apps_table.row.actions.uninstall=Desinstalar manage_window.apps_table.row.actions.uninstall=Desinstalar
@@ -8,8 +9,11 @@ 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.uninstall.popup.body=¿Eliminar {}?
manage_window.apps_table.row.actions.downgrade=Revertir versión 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.apps_table.row.actions.downgrade.popup.body=¿Realmente quieres revertir la versión actual de {}?
manage_window.apps_table.row.actions.install=Instalar
manage_window.apps_table.upgrade_toggle.tooltip=Haz clic para marcar o desmarcar la actualización manage_window.apps_table.upgrade_toggle.tooltip=Haz clic para marcar o desmarcar la actualización
manage_window.checkbox.only_apps=Aplicativos manage_window.checkbox.only_apps=Aplicativos
window_manage.input_search.placeholder=Buscar
window_manage.input_search.tooltip=Escriba y oprima ENTER para buscar aplicativos
manage_window.label.updates=Actualizaciones manage_window.label.updates=Actualizaciones
manage_window.status.refreshing=Recargando manage_window.status.refreshing=Recargando
manage_window.status.upgrading=Actualizando manage_window.status.upgrading=Actualizando
@@ -17,6 +21,8 @@ manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revirtiendo manage_window.status.downgrading=Revirtiendo
manage_window.status.info=Obteniendo información manage_window.status.info=Obteniendo información
manage_window.status.history=Obteniendo la historia manage_window.status.history=Obteniendo la historia
manage_window.status.searching=Buscando
manage_window.status.installing=Instalando
manage_window.bt.refresh.tooltip=Recarga los datos sobre los aplicativos instalados manage_window.bt.refresh.tooltip=Recarga los datos sobre los aplicativos instalados
manage_window.bt.upgrade.tooltip=Actualiza todos los aplicativos seleccionados manage_window.bt.upgrade.tooltip=Actualiza todos los aplicativos seleccionados
popup.flatpak_not_installed.title=Error popup.flatpak_not_installed.title=Error
@@ -35,12 +41,14 @@ popup.button.cancel=Cancelar
tray.action.manage=Administrar aplicativos tray.action.manage=Administrar aplicativos
tray.action.exit=Salir tray.action.exit=Salir
tray.action.about=Sobre tray.action.about=Sobre
tray.action.refreshing=Recargando
notification.new_updates=Actualizaciones encontradas notification.new_updates=Actualizaciones encontradas
flatpak.info.arch=arquitectura flatpak.info.arch=arquitectura
flatpak.info.branch=rama flatpak.info.branch=rama
flatpak.info.collection=colección flatpak.info.collection=colección
flatpak.info.commit=commit flatpak.info.commit=commit
flatpak.info.date=fecha flatpak.info.date=fecha
flatpak.info.description=descripción
flatpak.info.id=id flatpak.info.id=id
flatpak.info.installation=instalación flatpak.info.installation=instalación
flatpak.info.installed=instalado flatpak.info.installed=instalado
@@ -57,4 +65,6 @@ flatpak.info.version=versión
about.info.desc=Interfaz grafica no oficial para administración de aplicativos Flatpak about.info.desc=Interfaz grafica no oficial para administración de aplicativos Flatpak
about.info.link=Mas informaciones en about.info.link=Mas informaciones en
about.info.license=Licencia gratuita about.info.license=Licencia gratuita
about.info.rate=Si esta herramienta es útil para ti, dale una estrella en Github para mantenerla about.info.rate=Si esta herramienta es útil para ti, dale una estrella en Github para mantenerla
yes=sí
no=no

View File

@@ -1,6 +1,7 @@
manage_window.title=Gerenciador de Aplicativos Flatpak manage_window.title=Gerenciador de Aplicativos Flatpak
manage_window.columns.latest_version=Última Versão manage_window.columns.latest_version=Última Versão
manage_window.columns.update=Atualizar ? manage_window.columns.update=Atualizar ?
manage_window.columns.installed=Instalado
manage_window.apps_table.row.actions.info=Informação manage_window.apps_table.row.actions.info=Informação
manage_window.apps_table.row.actions.history=Histórico manage_window.apps_table.row.actions.history=Histórico
manage_window.apps_table.row.actions.uninstall=Desinstalar manage_window.apps_table.row.actions.uninstall=Desinstalar
@@ -8,8 +9,11 @@ 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.uninstall.popup.body=Remover {}?
manage_window.apps_table.row.actions.downgrade=Reverter versão 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.apps_table.row.actions.downgrade.popup.body=Você realmente quer reverter a versão atual de {} ?
manage_window.apps_table.row.actions.install=Instalar
manage_window.apps_table.upgrade_toggle.tooltip=Clique para marcar ou desmarcar a atualização manage_window.apps_table.upgrade_toggle.tooltip=Clique para marcar ou desmarcar a atualização
manage_window.checkbox.only_apps=Aplicativos manage_window.checkbox.only_apps=Aplicativos
window_manage.input_search.placeholder=Buscar
window_manage.input_search.tooltip=Digite e pressione ENTER para buscar aplicativos
manage_window.label.updates=Atualizações manage_window.label.updates=Atualizações
manage_window.status.refreshing=Recarregando manage_window.status.refreshing=Recarregando
manage_window.status.upgrading=Atualizando manage_window.status.upgrading=Atualizando
@@ -17,6 +21,8 @@ manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revertendo manage_window.status.downgrading=Revertendo
manage_window.status.info=Obtendo informação manage_window.status.info=Obtendo informação
manage_window.status.history=Obtendo histórico manage_window.status.history=Obtendo histórico
manage_window.status.searching=Buscando
manage_window.status.installing=Instalando
manage_window.bt.refresh.tooltip=Recarrega os dados sobre os aplicativos instalados manage_window.bt.refresh.tooltip=Recarrega os dados sobre os aplicativos instalados
manage_window.bt.upgrade.tooltip=Atualiza todos os aplicativos selecionados manage_window.bt.upgrade.tooltip=Atualiza todos os aplicativos selecionados
popup.flatpak_not_installed.title=Erro popup.flatpak_not_installed.title=Erro
@@ -35,12 +41,14 @@ popup.button.cancel=Cancelar
tray.action.manage=Gerenciar aplicativos tray.action.manage=Gerenciar aplicativos
tray.action.exit=Sair tray.action.exit=Sair
tray.action.about=Sobre tray.action.about=Sobre
tray.action.refreshing=Recarregando
notification.new_updates=Atualizações encontradas notification.new_updates=Atualizações encontradas
flatpak.info.arch=arquitetura flatpak.info.arch=arquitetura
flatpak.info.branch=ramo flatpak.info.branch=ramo
flatpak.info.collection=coleção flatpak.info.collection=coleção
flatpak.info.commit=commit flatpak.info.commit=commit
flatpak.info.date=data flatpak.info.date=data
flatpak.info.description=descrição
flatpak.info.id=id flatpak.info.id=id
flatpak.info.installation=instalação flatpak.info.installation=instalação
flatpak.info.installed=instalado flatpak.info.installed=instalado
@@ -57,4 +65,6 @@ flatpak.info.version=versão
about.info.desc=Interface gráfica não oficial para gerenciamento de aplicativos Flatpak about.info.desc=Interface gráfica não oficial para gerenciamento de aplicativos Flatpak
about.info.link=Mais informações em about.info.link=Mais informações em
about.info.license=Licença gratuita about.info.license=Licença gratuita
about.info.rate=Se essa ferramenta é útil para você, dê uma estrela no Github para mantê-la de pé about.info.rate=Se essa ferramenta é útil para você, dê uma estrela no Github para mantê-la de pé
yes=sim
no=não

View File

@@ -6,7 +6,7 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \ from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QHeaderView QHeaderView
from fpakman.core import resource from fpakman.core import resource, util
from fpakman.view.qt import dialog from fpakman.view.qt import dialog
@@ -44,8 +44,9 @@ class AppsTable(QTableWidget):
'manage_window.columns.latest_version', 'manage_window.columns.latest_version',
'flatpak.info.branch', 'flatpak.info.branch',
'flatpak.info.arch', 'flatpak.info.arch',
'flatpak.info.ref', 'flatpak.info.id',
'flatpak.info.origin', 'flatpak.info.origin',
'manage_window.columns.installed',
'manage_window.columns.update']] 'manage_window.columns.update']]
self.setColumnCount(len(self.column_names)) self.setColumnCount(len(self.column_names))
self.setFocusPolicy(Qt.NoFocus) self.setFocusPolicy(Qt.NoFocus)
@@ -62,33 +63,40 @@ class AppsTable(QTableWidget):
self.icon_cache = {} self.icon_cache = {}
def contextMenuEvent(self, QContextMenuEvent): # selected row right click event def contextMenuEvent(self, QContextMenuEvent): # selected row right click event
napps = len([app for app in self.window.apps if not app['model']['runtime']])
menu_row = QMenu()
action_info = QAction(self.window.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.window.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)
app = self.get_selected_app() app = self.get_selected_app()
action_uninstall = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.uninstall"]) menu_row = QMenu()
action_uninstall.setIcon(QIcon(resource.get_path('img/uninstall.svg')))
action_uninstall.triggered.connect(self._uninstall_app)
action_uninstall.setEnabled(not napps or not app['model']['runtime']) # only enabled for runtimes when no apps are available
menu_row.addAction(action_uninstall)
if not app['model']['runtime']: # not available for runtimes if app['model']['installed']:
action_downgrade = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.downgrade"]) napps = len([app for app in self.window.apps if not app['model']['runtime']])
action_downgrade.triggered.connect(self._downgrade_app)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg'))) action_info = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.info"])
menu_row.addAction(action_downgrade) 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.window.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.window.locale_keys["manage_window.apps_table.row.actions.uninstall"])
action_uninstall.setIcon(QIcon(resource.get_path('img/uninstall.svg')))
action_uninstall.triggered.connect(self._uninstall_app)
action_uninstall.setEnabled(not napps or not app['model']['runtime']) # only enabled for runtimes when no apps are available
menu_row.addAction(action_uninstall)
if not app['model']['runtime']: # not available for runtimes
action_downgrade = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.downgrade"])
action_downgrade.triggered.connect(self._downgrade_app)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
menu_row.addAction(action_downgrade)
else:
action_install = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.install"])
action_install.setIcon(QIcon(resource.get_path('img/install.svg')))
action_install.triggered.connect(self._install_app)
menu_row.addAction(action_install)
menu_row.adjustSize() menu_row.adjustSize()
menu_row.popup(QCursor.pos()) menu_row.popup(QCursor.pos())
@@ -122,6 +130,9 @@ class AppsTable(QTableWidget):
def _get_app_history(self): def _get_app_history(self):
self.window.get_app_history(self.get_selected_app()) self.window.get_app_history(self.get_selected_app())
def _install_app(self):
self.window.install_app(self.get_selected_app())
def _load_icon(self, http_response): def _load_icon(self, http_response):
icon_url = http_response.url().toString() icon_url = http_response.url().toString()
pixmap = QPixmap() pixmap = QPixmap()
@@ -142,9 +153,12 @@ class AppsTable(QTableWidget):
if apps: if apps:
for idx, app in enumerate(apps): for idx, app in enumerate(apps):
tooltip = util.strip_html(app['model']['description']) if app['model']['description'] else None
col_name = QTableWidgetItem() col_name = QTableWidgetItem()
col_name.setText(app['model']['name']) col_name.setText(app['model']['name'])
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_name.setToolTip(tooltip)
if not app['model']['icon']: if not app['model']['icon']:
col_name.setIcon(self.icon_flathub) col_name.setIcon(self.icon_flathub)
@@ -162,11 +176,13 @@ class AppsTable(QTableWidget):
col_version = QTableWidgetItem() col_version = QTableWidgetItem()
col_version.setText(app['model']['version']) col_version.setText(app['model']['version'])
col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_version.setToolTip(tooltip)
self.setItem(idx, 1, col_version) self.setItem(idx, 1, col_version)
col_release = QTableWidgetItem() col_release = QTableWidgetItem()
col_release.setText(app['model']['latest_version']) col_release.setText(app['model']['latest_version'])
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_release.setToolTip(tooltip)
self.setItem(idx, 2, col_release) self.setItem(idx, 2, col_release)
if app['model']['version'] and app['model']['latest_version'] and app['model']['version'] < app['model']['latest_version']: if app['model']['version'] and app['model']['latest_version'] and app['model']['version'] < app['model']['latest_version']:
@@ -175,25 +191,38 @@ class AppsTable(QTableWidget):
col_branch = QTableWidgetItem() col_branch = QTableWidgetItem()
col_branch.setText(app['model']['branch']) col_branch.setText(app['model']['branch'])
col_branch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_branch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_branch.setToolTip(tooltip)
self.setItem(idx, 3, col_branch) self.setItem(idx, 3, col_branch)
col_arch = QTableWidgetItem() col_arch = QTableWidgetItem()
col_arch.setText(app['model']['arch']) col_arch.setText(app['model']['arch'])
col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_arch.setToolTip(tooltip)
self.setItem(idx, 4, col_arch) self.setItem(idx, 4, col_arch)
col_package = QTableWidgetItem() col_id = QTableWidgetItem()
col_package.setText(app['model']['ref']) col_id.setText(app['model']['id'])
col_package.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_id.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.setItem(idx, 5, col_package) col_id.setToolTip(tooltip)
self.setItem(idx, 5, col_id)
col_origin = QTableWidgetItem() col_origin = QTableWidgetItem()
col_origin.setText(app['model']['origin']) col_origin.setText(app['model']['origin'])
col_origin.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) col_origin.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_origin.setToolTip(tooltip)
self.setItem(idx, 6, col_origin) self.setItem(idx, 6, col_origin)
col_installed = QTableWidgetItem()
col_installed.setText(self.parent().locale_keys['yes'] if app['model']['installed'] else self.parent().locale_keys['no'])
col_installed.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_installed.setTextAlignment(Qt.AlignCenter)
col_installed.setToolTip(tooltip)
col_installed.setForeground(Qt.blue if app['model']['installed'] else Qt.red)
self.setItem(idx, 7, col_installed)
col_update = UpdateToggleButton(app, self.window, self.window.locale_keys, app['model']['update']) if app['model']['update'] else None col_update = UpdateToggleButton(app, self.window, self.window.locale_keys, app['model']['update']) if app['model']['update'] else None
self.setCellWidget(idx, 7, col_update) self.setCellWidget(idx, 8, col_update)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents): def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
header_horizontal = self.horizontalHeader() header_horizontal = self.horizontalHeader()

View File

@@ -1,6 +1,6 @@
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QFormLayout, QGroupBox, \ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QFormLayout, QGroupBox, \
QLineEdit, QLabel QLineEdit, QLabel, QPlainTextEdit
class InfoDialog(QDialog): class InfoDialog(QDialog):
@@ -19,12 +19,17 @@ class InfoDialog(QDialog):
for attr in sorted(app.keys()): for attr in sorted(app.keys()):
if attr != 'name': if attr != 'name' and app[attr]:
text = QLineEdit() if attr == 'description':
text.setText(app[attr]) text = QPlainTextEdit()
text.setStyleSheet("width: 400px") text.appendHtml(app[attr])
else:
text = QLineEdit()
text.setText(app[attr])
text.setCursorPosition(0)
text.setStyleSheet("width: 400px")
text.setReadOnly(True) text.setReadOnly(True)
text.setCursorPosition(0)
label = QLabel("{}: ".format(locale_keys.get('flatpak.info.' + attr, attr)).capitalize()) label = QLabel("{}: ".format(locale_keys.get('flatpak.info.' + attr, attr)).capitalize())
label.setStyleSheet("font-weight: bold") label.setStyleSheet("font-weight: bold")

View File

@@ -6,7 +6,7 @@ from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from fpakman.core.controller import FlatpakManager from fpakman.core.controller import FlatpakManager
from fpakman.core import resource from fpakman.core import resource, system
from fpakman.view.qt.about import AboutDialog from fpakman.view.qt.about import AboutDialog
from fpakman.view.qt.window import ManageWindow from fpakman.view.qt.window import ManageWindow
@@ -33,6 +33,19 @@ class UpdateCheck(QThread):
time.sleep(self.check_interval) time.sleep(self.check_interval)
class LoadDatabase(QThread):
signal_finished = pyqtSignal()
def __init__(self, manager: FlatpakManager, parent=None):
super(LoadDatabase, self).__init__(parent)
self.manager = manager
def run(self):
self.manager.load_full_database()
self.signal_finished.emit()
class TrayIcon(QSystemTrayIcon): class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, manager: FlatpakManager, check_interval: int = 60): def __init__(self, locale_keys: dict, manager: FlatpakManager, check_interval: int = 60):
@@ -46,8 +59,12 @@ class TrayIcon(QSystemTrayIcon):
self.menu = QMenu() self.menu = QMenu()
self.action_refreshing = self.menu.addAction(self.locale_keys['tray.action.refreshing'] + '...')
self.action_refreshing.setEnabled(False)
self.action_manage = self.menu.addAction(self.locale_keys['tray.action.manage']) self.action_manage = self.menu.addAction(self.locale_keys['tray.action.manage'])
self.action_manage.triggered.connect(self.show_manage_window) self.action_manage.triggered.connect(self.show_manage_window)
self.action_manage.setVisible(False)
self.action_about = self.menu.addAction(self.locale_keys['tray.action.about']) self.action_about = self.menu.addAction(self.locale_keys['tray.action.about'])
self.action_about.triggered.connect(self.show_about) self.action_about.triggered.connect(self.show_about)
@@ -64,6 +81,16 @@ class TrayIcon(QSystemTrayIcon):
self.dialog_about = AboutDialog(self.locale_keys) self.dialog_about = AboutDialog(self.locale_keys)
self.thread_database = LoadDatabase(manager)
self.thread_database.signal_finished.connect(self._update_menu)
def load_database(self):
self.thread_database.start()
def _update_menu(self):
self.action_refreshing.setVisible(False)
self.action_manage.setVisible(True)
def notify_updates(self, updates: int): def notify_updates(self, updates: int):
if updates > 0: if updates > 0:
if self.icon().cacheKey() != self.icon_update.cacheKey(): if self.icon().cacheKey() != self.icon_update.cacheKey():
@@ -72,11 +99,7 @@ class TrayIcon(QSystemTrayIcon):
msg = '{}: {}'.format(self.locale_keys['notification.new_updates'], updates) msg = '{}: {}'.format(self.locale_keys['notification.new_updates'], updates)
self.setToolTip(msg) self.setToolTip(msg)
if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)): system.notify_user(msg)
os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_45.svg'), msg))
if self.manage_window:
self.manage_window.refresh()
else: else:
self.setIcon(self.icon_default) self.setIcon(self.icon_default)

View File

@@ -97,6 +97,7 @@ class GetAppInfo(QThread):
app_info = flatpak.get_app_info_fields(self.app['model']['id'], self.app['model']['branch']) app_info = flatpak.get_app_info_fields(self.app['model']['id'], self.app['model']['branch'])
app_info['name'] = self.app['model']['name'] app_info['name'] = self.app['model']['name']
app_info['type'] = 'runtime' if self.app['model']['runtime'] else 'app' app_info['type'] = 'runtime' if self.app['model']['runtime'] else 'app'
app_info['description'] = self.app['model']['description']
self.signal_finished.emit(app_info) self.signal_finished.emit(app_info)
self.app = None self.app = None
@@ -113,3 +114,42 @@ class GetAppHistory(QThread):
commits = flatpak.get_app_commits_data(self.app['model']['ref'], self.app['model']['origin']) 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.signal_finished.emit({'model': self.app['model'], 'commits': commits})
self.app = None self.app = None
class SearchApps(QThread):
signal_finished = pyqtSignal(list)
def __init__(self, manager: FlatpakManager):
super(SearchApps, self).__init__()
self.word = None
self.manager = manager
def run(self):
apps_found = []
if self.word:
apps_found = self.manager.search(self.word)
self.signal_finished.emit(apps_found)
self.word = None
class InstallApp(QThread):
signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self):
super(InstallApp, self).__init__()
self.app = None
def run(self):
if self.app:
for output in flatpak.install_and_stream(self.app['model']['id'], self.app['model']['origin']):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.app = None
self.signal_finished.emit()

View File

@@ -4,9 +4,9 @@ from threading import Lock
from typing import List from typing import List
from PyQt5.QtCore import QEvent from PyQt5.QtCore import QEvent
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QPlainTextEdit QSizePolicy, QLabel, QPlainTextEdit, QLineEdit
from fpakman.core import resource, flatpak from fpakman.core import resource, flatpak
from fpakman.core.controller import FlatpakManager from fpakman.core.controller import FlatpakManager
@@ -16,7 +16,7 @@ from fpakman.view.qt.history import HistoryDialog
from fpakman.view.qt.info import InfoDialog from fpakman.view.qt.info import InfoDialog
from fpakman.view.qt.root import is_root, ask_root_password from fpakman.view.qt.root import is_root, ask_root_password
from fpakman.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \ from fpakman.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
GetAppHistory GetAppHistory, SearchApps, InstallApp
class ManageWindow(QWidget): class ManageWindow(QWidget):
@@ -42,12 +42,43 @@ class ManageWindow(QWidget):
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
self.setLayout(self.layout) self.setLayout(self.layout)
self.toolbar_search = QToolBar()
self.toolbar_search.setStyleSheet("spacing: 0px;")
self.toolbar_search.setContentsMargins(0, 0, 0, 0)
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.toolbar_search.addWidget(spacer)
label_pre_search = QLabel()
label_pre_search.setStyleSheet("background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;")
self.toolbar_search.addWidget(label_pre_search)
self.input_search = QLineEdit()
self.input_search.setFrame(False)
self.input_search.setPlaceholderText(self.locale_keys['window_manage.input_search.placeholder']+"...")
self.input_search.setToolTip(self.locale_keys['window_manage.input_search.tooltip'])
self.input_search.setStyleSheet("QLineEdit { background-color: white; color: grey; spacing: 0;}")
self.input_search.returnPressed.connect(self.search)
self.toolbar_search.addWidget(self.input_search)
label_pos_search = QLabel()
label_pos_search.setPixmap(QPixmap(resource.get_path('img/search.svg')))
label_pos_search.setStyleSheet("background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;")
self.toolbar_search.addWidget(label_pos_search)
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.toolbar_search.addWidget(spacer)
self.layout.addWidget(self.toolbar_search)
toolbar = QToolBar()
self.checkbox_only_apps = QCheckBox() self.checkbox_only_apps = QCheckBox()
self.checkbox_only_apps.setText(self.locale_keys['manage_window.checkbox.only_apps']) self.checkbox_only_apps.setText(self.locale_keys['manage_window.checkbox.only_apps'])
self.checkbox_only_apps.setChecked(True) self.checkbox_only_apps.setChecked(True)
self.checkbox_only_apps.stateChanged.connect(self.filter_only_apps) self.checkbox_only_apps.stateChanged.connect(self.filter_only_apps)
toolbar = QToolBar()
toolbar.addWidget(self.checkbox_only_apps) toolbar.addWidget(self.checkbox_only_apps)
spacer = QWidget() spacer = QWidget()
@@ -56,7 +87,7 @@ class ManageWindow(QWidget):
self.label_status = QLabel() self.label_status = QLabel()
self.label_status.setText('') self.label_status.setText('')
self.label_status.setStyleSheet("color: orange") self.label_status.setStyleSheet("color: orange; font-weight: bold")
toolbar.addWidget(self.label_status) toolbar.addWidget(self.label_status)
spacer = QWidget() spacer = QWidget()
@@ -111,6 +142,13 @@ class ManageWindow(QWidget):
self.thread_get_history = GetAppHistory() self.thread_get_history = GetAppHistory()
self.thread_get_history.signal_finished.connect(self._finish_get_history) self.thread_get_history.signal_finished.connect(self._finish_get_history)
self.thread_search = SearchApps(self.manager)
self.thread_search.signal_finished.connect(self._finish_search)
self.thread_install = InstallApp()
self.thread_install.signal_output.connect(self._update_action_output)
self.thread_install.signal_finished.connect(self._finish_install)
self.toolbar_bottom = QToolBar() self.toolbar_bottom = QToolBar()
self.label_updates = QLabel('') self.label_updates = QLabel('')
self.label_updates.setStyleSheet("color: red;") self.label_updates.setStyleSheet("color: red;")
@@ -170,6 +208,14 @@ class ManageWindow(QWidget):
self.thread_lock.release() self.thread_lock.release()
def _hide_output(self):
self.textarea_output.clear()
self.textarea_output.hide()
def _show_output(self):
self.textarea_output.clear()
self.textarea_output.show()
def refresh(self, clear_output: bool = True): def refresh(self, clear_output: bool = True):
if self._acquire_lock(): if self._acquire_lock():
@@ -177,8 +223,7 @@ class ManageWindow(QWidget):
self._begin_action(self.locale_keys['manage_window.status.refreshing']) self._begin_action(self.locale_keys['manage_window.status.refreshing'])
if clear_output: if clear_output:
self.textarea_output.clear() self._hide_output()
self.textarea_output.hide()
self.thread_refresh.start() self.thread_refresh.start()
@@ -321,17 +366,23 @@ class ManageWindow(QWidget):
def _begin_action(self, action_label: str): def _begin_action(self, action_label: str):
self.label_status.setText(action_label + "...") self.label_status.setText(action_label + "...")
self.toolbar_search.setVisible(False)
self.bt_upgrade.setEnabled(False) self.bt_upgrade.setEnabled(False)
self.bt_refresh.setEnabled(False) self.bt_refresh.setEnabled(False)
self.checkbox_only_apps.setEnabled(False) self.checkbox_only_apps.setEnabled(False)
self.table_apps.setEnabled(False) self.table_apps.setEnabled(False)
def finish_action(self): def finish_action(self, clear_search: bool = True):
self.bt_refresh.setEnabled(True) self.bt_refresh.setEnabled(True)
self.toolbar_search.setVisible(True)
self.checkbox_only_apps.setEnabled(True) self.checkbox_only_apps.setEnabled(True)
self.table_apps.setEnabled(True) self.table_apps.setEnabled(True)
self.input_search.setEnabled(True)
self.label_status.setText('') self.label_status.setText('')
if clear_search:
self.input_search.setText('')
def downgrade_app(self, app: dict): def downgrade_app(self, app: dict):
self._check_flatpak_installed() self._check_flatpak_installed()
@@ -387,3 +438,37 @@ class ManageWindow(QWidget):
self.change_update_state() self.change_update_state()
dialog_history = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys) dialog_history = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys)
dialog_history.exec_() dialog_history.exec_()
def search(self):
word = self.input_search.text().strip()
if word and self._acquire_lock():
self.textarea_output.clear()
self.textarea_output.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.searching'])
self.thread_search.word = word
self.thread_search.start()
def _finish_search(self, apps_found: List[dict]):
self._release_lock()
self.finish_action(clear_search=False)
self.update_apps(apps_found)
def install_app(self, app: dict):
self._check_flatpak_installed()
if self._acquire_lock():
self._begin_action(self.locale_keys['manage_window.status.installing'])
self._show_output()
self.thread_install.app = app
self.thread_install.start()
def _finish_install(self):
self.input_search.setText('')
self.finish_action()
self._release_lock()
self.refresh(clear_output=False)