diff --git a/README.md b/README.md index 75b172d2..e050db9a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,20 @@ ## fpakman -Graphical interface for Flatpak application management. It has also a tray icon to let the user known when new updates are available. -Wriiten in Python for QT5. +Graphical interface for Flatpak application management. It is a tray icon to let the user known when new updates are available. +It has also a management window allowing the user to see all installed apllications and update them. + +### Technologies: +- Python3 and QT 5. + +## Settings +You can change some application settings via environment variables: +- **FPAKMAN_UPDATE_NOTIFICATION: enable or disable system updates notifications. Use 0 (disable) or 1 (enable, default). +- **FPAKMAN_CHECK_INTERVAL**: define the updates check interval in seconds. Default: 60. + +## Installation script +You can install the application without compromising your system via the provided installation script called 'sandbox_installer.py'. +Type in the terminal: sudo python3 sandbox_installer.py. If you want to uninstall the application, just call the script the same way. ### Roadmap - Test installer for Ubuntu - Locales -- System notification whe new updates - Search and install applications. \ No newline at end of file diff --git a/app.py b/app.py index 7e9c272f..e1d42cab 100755 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ #!env/bin/python import argparse +import os import sys from PyQt5.QtWidgets import QApplication, QWidget @@ -18,7 +19,7 @@ manager = FlatpakManager() manager.load_database_async() controller = FlatpakController(manager) hidden_widget = QWidget() -trayIcon = TrayIcon(parent=hidden_widget, controller=controller, check_interval=30) +trayIcon = TrayIcon(parent=hidden_widget, controller=controller, check_interval=int(os.getenv('FPAKMAN_CHECK_INTERVAL', 30))) trayIcon.show() sys.exit(app.exec_()) diff --git a/core/model.py b/core/model.py index b8be52b6..95e55b2f 100755 --- a/core/model.py +++ b/core/model.py @@ -21,7 +21,7 @@ class FlatpakManager: self.lock_db_read = Lock() def load_database_async(self): - Thread(target=self.load_database).start() + Thread(target=self.load_database, daemon=True).start() def load_database(self): diff --git a/resources/img/flathub_25.svg b/resources/img/flathub_10.svg similarity index 97% rename from resources/img/flathub_25.svg rename to resources/img/flathub_10.svg index 15e46ff5..2311083c 100644 --- a/resources/img/flathub_25.svg +++ b/resources/img/flathub_10.svg @@ -9,13 +9,13 @@ 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="24.856121" - height="24.071226" - viewBox="0 0 6.5765152 6.3688453" + 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_25.svg"> + sodipodi:docname="flathub_5.svg"> + transform="translate(-12.162843,-177.89043)"> image/svg+xml - + @@ -62,19 +62,19 @@ + style="fill:#ff2a2a;stroke:#800000;stroke-width:0.77710575"> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> @@ -92,7 +92,7 @@ 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#aa0000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> @@ -172,7 +172,7 @@ 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#aa0000;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" /> + 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:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;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" /> diff --git a/view/qt/systray.py b/view/qt/systray.py index 8c438d51..76ebf5e0 100755 --- a/view/qt/systray.py +++ b/view/qt/systray.py @@ -1,3 +1,4 @@ +import os import time from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication @@ -6,7 +7,6 @@ from PyQt5.QtWidgets import QSystemTrayIcon, QMenu from core import resource - # to update from core.controller import FlatpakController from view.qt.window import ManageWindow @@ -57,12 +57,18 @@ class TrayIcon(QSystemTrayIcon): def notify_updates(self, updates: int): if updates > 0: - if self.icon() != self.icon_update: + if self.icon().cacheKey() != self.icon_update.cacheKey(): self.setIcon(self.icon_update) - self.setToolTip('Updates found: {}'.format(updates)) - if self.manage_window: + msg = 'Updates found: {}'.format(updates) + self.setToolTip(msg) + + if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)): + os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_10.svg'), msg)) + + if self.manage_window and not self.manage_window.isHidden(): self.manage_window.refresh() + else: self.setIcon(self.icon_default) self.setToolTip(None)