**Features**
- Management panel shows update commands streams
- Management panel status label is "orange" now

**Fixes**
- Application name is not properly showing for Flatpak  1.2.X
This commit is contained in:
Vinícius Moreira
2019-06-18 19:02:16 -03:00
committed by GitHub
parent 3345e39dbf
commit f36991ea1f
17 changed files with 122 additions and 229 deletions

View File

@@ -4,10 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.2.0] - 2019-06-18
### Features
- Management panel shows update commands streams
- Management panel status label is "orange" now
### Fixes
- Application name is not properly showing for Flatpak 1.2.X
## [0.1.0] - 2019-06-14 ## [0.1.0] - 2019-06-14
### Features ### Features
- System tray icon. - System tray icon.
- Applications management window. - Applications management window.
- Support for the following locales: PT, EN, ES. - Support for the following locales: PT, EN, ES.
- System notification for new updates. - System notification for new updates.
- Update applications. - Update applications.

View File

@@ -17,7 +17,13 @@ It has also a management window allowing the user to see all installed applicati
- python-pyqt5 - python-pyqt5
### Distribution ### Distribution
Currently available via **PyPi** (sudo pip3 install fpakman) or **AUR** (for Arch Linux users. **fpakman-staging** is just for testing, do not install it.) **PyPi**
```
sudo pip3 install fpakman
```
**AUR**
As **fpakman** package. There is also a staging version (**fpakman-staging**) but is intended for testing and it may not work properly.
### Manual installation: ### Manual installation:
@@ -38,6 +44,5 @@ You can change some application settings via environment variables:
- **FPAKMAN_CHECK_INTERVAL**: define the updates check interval in seconds. Default: 60. - **FPAKMAN_CHECK_INTERVAL**: define the updates check interval in seconds. Default: 60.
### Roadmap ### Roadmap
- Show updates being applied
- Search and install applications - Search and install applications
- Uninstall applications - Uninstall applications

View File

@@ -1,5 +1,5 @@
# used for AUR installation # used for AUR installation
import subprocess import os
import sys import sys
from pathlib import Path from pathlib import Path
@@ -7,14 +7,18 @@ desktop_file = """
[Desktop Entry] [Desktop Entry]
Type = Application Type = Application
Name = fpakman Name = fpakman
Categories = System;
Comment = Manage your Flatpak applications Comment = Manage your Flatpak applications
Exec = /usr/bin/fpakman Exec = /usr/bin/fpakman
Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/flathub_45.svg Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/flathub_45.svg
""".format(version="{}.{}".format(sys.version_info.major, sys.version_info.minor)) """.format(version="{}.{}".format(sys.version_info.major, sys.version_info.minor))
file_path = '{}/.local/share/applications/fpakman.desktop'.format(str(Path.home())) apps_path = '{}/.local/share/applications'.format(str(Path.home()))
if not os.path.exists(apps_path):
os.mkdir(apps_path)
file_path = '{}/fpakman.desktop'.format(apps_path)
with open(file_path, 'w+') as f: with open(file_path, 'w+') as f:
f.write(desktop_file) f.write(desktop_file)
subprocess.run('desktop-file-install {}'.format(file_path), shell=True)

View File

@@ -0,0 +1 @@
__version__ = '0.2.0'

View File

@@ -2,13 +2,15 @@ import argparse
import os import os
import sys import sys
from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication
from fpakman import __version__
from fpakman.core import resource
from fpakman.core import util
from fpakman.core.controller import FlatpakController from fpakman.core.controller import FlatpakController
from fpakman.core.model import FlatpakManager from fpakman.core.model import FlatpakManager
from fpakman.view.qt.systray import TrayIcon from fpakman.view.qt.systray import TrayIcon
from fpakman.core import __version__
from fpakman.core import util
parser = argparse.ArgumentParser(prog='fpakman', description="GUI for Flatpak applications management") parser = argparse.ArgumentParser(prog='fpakman', description="GUI for Flatpak applications management")
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__)) parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__))
@@ -17,13 +19,12 @@ args = parser.parse_args()
locale_keys = util.get_locale_keys() locale_keys = util.get_locale_keys()
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setWindowIcon(QIcon(resource.get_path('img/flathub_45.svg')))
manager = FlatpakManager() manager = FlatpakManager()
manager.load_database_async() manager.load_database_async()
controller = FlatpakController(manager) controller = FlatpakController(manager)
hidden_widget = QWidget()
trayIcon = TrayIcon(locale_keys=locale_keys, trayIcon = TrayIcon(locale_keys=locale_keys,
parent=hidden_widget,
controller=controller, controller=controller,
check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60))) check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 60)))
trayIcon.show() trayIcon.show()

View File

@@ -1 +1 @@
__version__ = '0.1.0'

View File

@@ -11,8 +11,8 @@ class FlatpakController:
def refresh(self) -> List[dict]: def refresh(self) -> List[dict]:
return self.model.read_installed() return self.model.read_installed()
def update(self, package_refs: List[str]) -> List[dict]: def update(self, app_ref: str):
return self.model.update_apps(package_refs) return self.model.update_app(app_ref)
def check_installed(self) -> bool: def check_installed(self) -> bool:
version = self.model.get_version() version = self.model.get_version()

View File

@@ -23,7 +23,7 @@ def app_str_to_json(line: str, version: str) -> dict:
app['name'] = ref_data[0].split('.')[-1] app['name'] = ref_data[0].split('.')[-1]
app['version'] = None app['version'] = None
elif '1.2' <= version < '1.3': elif '1.2' <= version < '1.3':
app = {'name': app_array[0][1].split('.')[-1], app = {'name': app_array[1].strip().split('.')[-1],
'id': app_array[1], 'id': app_array[1],
'version': app_array[2], 'version': app_array[2],
'branch': app_array[3], 'branch': app_array[3],
@@ -68,8 +68,17 @@ def list_installed() -> List[dict]:
return [] return []
def update(ref: str): def update(app_ref: str) -> bool:
return bool(system.run_cmd('flatpak update -y ' + ref)) return bool(system.run_cmd('flatpak update -y ' + app_ref))
def update_and_stream(app_ref: str):
"""
Updates the app reference and streams Flatpak output,
:param app_ref:
:return:
"""
return system.stream_cmd(['flatpak', 'update', '-y', app_ref])
def list_updates_as_str(): def list_updates_as_str():

View File

@@ -90,3 +90,19 @@ class FlatpakManager:
return [*self.apps] return [*self.apps]
return [] return []
def update_app(self, ref: str):
"""
:param ref:
:return: the update command stream
"""
if self.apps:
package_found = [app for app in self.apps if app['ref'] == ref]
if package_found:
return flatpak.update_and_stream(ref)
return None

View File

@@ -1,10 +1,7 @@
import os import os
app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def get_path(resource_path): def get_path(resource_path):
app_dir = get_app_dir()
return app_dir + '/resources/' + resource_path return app_dir + '/resources/' + resource_path
def get_app_dir():
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

View File

@@ -1,6 +1,11 @@
import subprocess import subprocess
from typing import List
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) res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
return res.stdout.decode() if ignore_return_code or res.returncode == expected_code else None 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

View File

@@ -8,10 +8,11 @@ def get_locale_keys():
current_locale = locale.getdefaultlocale() current_locale = locale.getdefaultlocale()
locale_path = None locale_path = None
if current_locale: if current_locale:
current_locale = current_locale[0] current_locale = current_locale[0]
locale_dir = '{}/resources/locale'.format(resource.get_app_dir()) locale_dir = resource.get_path('locale')
for locale_file in glob.glob(locale_dir + '/*'): for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1] name = locale_file.split('/')[-1]
@@ -21,7 +22,7 @@ def get_locale_keys():
break break
if not locale_path: if not locale_path:
locale_path = '{}/resources/locale/en'.format(resource.get_app_dir()) locale_path = resource.get_path('locale/en')
with open(locale_path, 'r') as f: with open(locale_path, 'r') as f:
locale_keys = f.readlines() locale_keys = f.readlines()

View File

@@ -1,179 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
width="9.6111898"
height="8.805685"
viewBox="0 0 2.5429606 2.3298375"
version="1.1"
id="svg3871"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="flathub_5.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="354.03019"
inkscape:cy="118.62781"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showborder="false"
inkscape:window-width="1360"
inkscape:window-height="707"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-bottom="-0.2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
units="px" />
<metadata
id="metadata3868">
<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>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-12.162843,-177.89043)">
<g
transform="matrix(0.01780156,0,0,0.02142021,-1.8040418,160.22597)"
id="g10674"
style="stroke-width:0.77710575">
<path
sodipodi:nodetypes="ccccccccccc"
inkscape:connector-curvature="0"
id="path10642"
d="m 856.01397,830.22631 -66.76365,43.44891 v 34.93973 l 33.38182,21.72315 33.37926,-21.72315 v -0.005 l 0.003,0.003 33.38183,21.72574 33.37925,-21.72315 v -34.93979 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:9.3252697;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,429.63205,432.90461)"
id="g10652">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10644"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10648"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10650"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10646"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,463.01368,454.62849)"
id="g10662">
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="279.42584"
x="1289.9076"
height="79.110413"
width="79.110413"
id="rect10654"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10656"
width="79.110413"
height="79.110413"
x="1226.2882"
y="215.8064"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
inkscape:connector-curvature="0"
id="path10658"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 783.00003,826.54445 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
id="path10660"
inkscape:connector-curvature="0" />
</g>
<g
style="stroke-width:0.58728963"
id="g10672"
transform="matrix(0.50221552,0,0,0.50636711,396.25042,454.62849)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10664"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10666"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10668"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10670"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -7,7 +7,7 @@ manage_window.columns.ref=Ref
manage_window.columns.origin=Origem manage_window.columns.origin=Origem
manage_window.columns.update=Atualizar ? manage_window.columns.update=Atualizar ?
manage_window.checkbox.only_apps=Apps manage_window.checkbox.only_apps=Apps
manage_window.label.updates=Actualizaciones manage_window.label.updates=Atualizações
manage_window.status.refreshing=Recarregando manage_window.status.refreshing=Recarregando
manage_window.status.updating=Atualizando manage_window.status.updating=Atualizando
popup.flatpak_not_installed.title=Erro popup.flatpak_not_installed.title=Erro

View File

@@ -34,15 +34,16 @@ class UpdateCheck(QThread):
class TrayIcon(QSystemTrayIcon): class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, controller: FlatpakController, check_interval: int = 60, parent=None): def __init__(self, locale_keys: dict, controller: FlatpakController, check_interval: int = 60):
super(TrayIcon, self).__init__()
self.locale_keys = locale_keys self.locale_keys = locale_keys
self.controller = controller self.controller = controller
self.icon_default = QIcon(resource.get_path('img/flathub_45.svg')) self.icon_default = QIcon(resource.get_path('img/flathub_45.svg'))
self.icon_update = QIcon(resource.get_path('img/update_logo.svg')) self.icon_update = QIcon(resource.get_path('img/update_logo.svg'))
QSystemTrayIcon.__init__(self, self.icon_default, parent) self.setIcon(self.icon_default)
self.menu = QMenu(parent) self.menu = QMenu()
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_exit = self.menu.addAction(self.locale_keys['tray.action.exit']) self.action_exit = self.menu.addAction(self.locale_keys['tray.action.exit'])
@@ -63,7 +64,7 @@ class TrayIcon(QSystemTrayIcon):
self.setToolTip(msg) self.setToolTip(msg)
if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)): if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)):
os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_10.svg'), msg)) os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_45.svg'), msg))
if self.manage_window: if self.manage_window:
self.manage_window.refresh() self.manage_window.refresh()

View File

@@ -8,9 +8,9 @@ from PyQt5.QtGui import QIcon, QColor, QPixmap, QWindowStateChangeEvent
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QTableWidget, \ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QTableWidget, \
QTableWidgetItem, QTableView, QCheckBox, QHeaderView, QToolButton, QToolBar, \ QTableWidgetItem, QTableView, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QMessageBox QSizePolicy, QLabel, QMessageBox, QPlainTextEdit
from fpakman.core import __version__ from fpakman import __version__
from fpakman.core import resource from fpakman.core import resource
from fpakman.core.controller import FlatpakController from fpakman.core.controller import FlatpakController
@@ -86,6 +86,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")
toolbar.addWidget(self.label_status) toolbar.addWidget(self.label_status)
spacer = QWidget() spacer = QWidget()
@@ -94,7 +95,7 @@ class ManageWindow(QWidget):
self.bt_refresh = QToolButton() self.bt_refresh = QToolButton()
self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg'))) self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
self.bt_refresh.clicked.connect(self.refresh) self.bt_refresh.clicked.connect(lambda: self.refresh(clear_output=True))
toolbar.addWidget(self.bt_refresh) toolbar.addWidget(self.bt_refresh)
self.bt_update = QToolButton() self.bt_update = QToolButton()
@@ -118,8 +119,16 @@ class ManageWindow(QWidget):
self.layout.addWidget(self.table_apps) self.layout.addWidget(self.table_apps)
self.textarea_output = QPlainTextEdit(self)
self.textarea_output.resize(self.table_apps.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.thread_update = UpdateSelectedApps(self.controller) self.thread_update = UpdateSelectedApps(self.controller)
self.thread_update.signal.connect(self._finish_update_selected) self.thread_update.signal_output.connect(self._update_action_output)
self.thread_update.signal_finished.connect(self._finish_update_selected)
self.thread_refresh = RefreshApps(self.controller) self.thread_refresh = RefreshApps(self.controller)
self.thread_refresh.signal.connect(self._finish_refresh) self.thread_refresh.signal.connect(self._finish_refresh)
@@ -202,11 +211,16 @@ class ManageWindow(QWidget):
self.thread_lock.release() self.thread_lock.release()
def refresh(self): def refresh(self, clear_output: bool = True):
if self._acquire_lock(): if self._acquire_lock():
self._check_flatpak_installed() 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()
self.textarea_output.hide()
self.thread_refresh.start() self.thread_refresh.start()
def _finish_refresh(self): def _finish_refresh(self):
@@ -248,6 +262,8 @@ class ManageWindow(QWidget):
self.bt_update.setEnabled(enable_bt_update) self.bt_update.setEnabled(enable_bt_update)
self.tray_icon.notify_updates(updates)
def centralize(self): def centralize(self):
geo = self.frameGeometry() geo = self.frameGeometry()
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
@@ -338,21 +354,24 @@ class ManageWindow(QWidget):
if self._acquire_lock(): if self._acquire_lock():
if self.apps: if self.apps:
to_update = [pak['model']['ref'] for pak in self.apps if pak['visible'] and pak['update_checked']] to_update = [pak['model']['ref'] for pak in self.apps if pak['visible'] and pak['update_checked']]
if to_update: if to_update:
self.textarea_output.clear()
self.textarea_output.setVisible(True)
self._begin_action(self.locale_keys['manage_window.status.updating'] + '...') self._begin_action(self.locale_keys['manage_window.status.updating'] + '...')
self.thread_update.refs_to_update = to_update self.thread_update.refs_to_update = to_update
self.thread_update.start() self.thread_update.start()
def _finish_update_selected(self): def _finish_update_selected(self):
self.update_apps(self.thread_update.updated_apps)
self.finish_action() self.finish_action()
if self.tray_icon and self.thread_update.updated_apps:
self.tray_icon.notify_updates(len([app for app in self.thread_update.updated_apps if app['update']]))
self._release_lock() self._release_lock()
self.refresh(clear_output=False)
def _update_action_output(self, output: str):
self.textarea_output.appendPlainText(output)
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)
@@ -369,20 +388,25 @@ class ManageWindow(QWidget):
# Threaded actions # Threaded actions
class UpdateSelectedApps(QThread): class UpdateSelectedApps(QThread):
signal = pyqtSignal() signal_finished = pyqtSignal()
signal_output = pyqtSignal(str)
def __init__(self, controller: FlatpakController): def __init__(self, controller: FlatpakController):
super(UpdateSelectedApps, self).__init__() super(UpdateSelectedApps, self).__init__()
self.controller = controller self.controller = controller
self.refs_to_update = [] self.refs_to_update = []
self.updated_apps = None
def run(self): def run(self):
self.updated_apps = self.controller.update(self.refs_to_update)
self.signal.emit() for app_ref in self.refs_to_update:
for output in self.controller.update(app_ref):
line = output.decode().strip()
if line:
self.signal_output.emit(line)
self.signal_finished.emit()
class RefreshApps(QThread): class RefreshApps(QThread):

View File

@@ -15,7 +15,7 @@ with open(file_dir + '/requirements.txt', 'r') as f:
requirements = [line.strip() for line in f.readlines() if line] requirements = [line.strip() for line in f.readlines() if line]
with open(file_dir + '/fpakman/core/__init__.py', 'r') as f: with open(file_dir + '/fpakman/__init__.py', 'r') as f:
exec(f.readlines()[0]) exec(f.readlines()[0])