mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 01:14:15 +02:00
Management panel shows update commands streams
This commit is contained in:
@@ -4,6 +4,10 @@ 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/).
|
||||
|
||||
## [0.2.0] - 2019-06-18
|
||||
### Features
|
||||
- Management panel shows update commands streams
|
||||
|
||||
## [0.1.0] - 2019-06-14
|
||||
### Features
|
||||
- System tray icon.
|
||||
|
||||
@@ -17,7 +17,13 @@ It has also a management window allowing the user to see all installed applicati
|
||||
- python-pyqt5
|
||||
|
||||
### 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:
|
||||
@@ -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.
|
||||
|
||||
### Roadmap
|
||||
- Show updates being applied
|
||||
- Search and install applications
|
||||
- Uninstall applications
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
# used for AUR installation
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
share_path = str(Path.home()) + '/.local/share'
|
||||
local_resources_path = share_path + '/fpakman'
|
||||
|
||||
if os.path.exists(local_resources_path):
|
||||
shutil.rmtree(local_resources_path)
|
||||
|
||||
os.mkdir(local_resources_path)
|
||||
|
||||
desktop_file = """
|
||||
[Desktop Entry]
|
||||
Type = Application
|
||||
@@ -19,20 +11,19 @@ Name = fpakman
|
||||
Categories = System;
|
||||
Comment = Manage your Flatpak applications
|
||||
Exec = /usr/bin/fpakman
|
||||
Icon = {resource_path}/resources/img/flathub_45.svg
|
||||
""".format(resource_path=local_resources_path)
|
||||
Icon = /usr/lib/python{version}/site-packages/fpakman/resources/img/flathub_45.svg
|
||||
""".format(version="{}.{}".format(sys.version_info.major, sys.version_info.minor))
|
||||
|
||||
apps_path = share_path + '/applications'
|
||||
apps_path = '{}/.local/share/applications'.format(str(Path.home()))
|
||||
|
||||
if not os.path.exists(apps_path):
|
||||
os.mkdir(apps_path)
|
||||
|
||||
file_path = apps_path + '/fpakman.desktop'
|
||||
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
file_path = '{}/fpakman.desktop'.format(apps_path)
|
||||
|
||||
with open(file_path, 'w+') as f:
|
||||
f.write(desktop_file)
|
||||
|
||||
subprocess.run('desktop-file-install {}'.format(file_path), shell=True)
|
||||
res = subprocess.run('desktop-file-install {}'.format(file_path), shell=True)
|
||||
|
||||
exit(res.returncode)
|
||||
|
||||
@@ -11,8 +11,8 @@ class FlatpakController:
|
||||
def refresh(self) -> List[dict]:
|
||||
return self.model.read_installed()
|
||||
|
||||
def update(self, package_refs: List[str]) -> List[dict]:
|
||||
return self.model.update_apps(package_refs)
|
||||
def update(self, app_ref: str):
|
||||
return self.model.update_app(app_ref)
|
||||
|
||||
def check_installed(self) -> bool:
|
||||
version = self.model.get_version()
|
||||
|
||||
@@ -68,8 +68,17 @@ def list_installed() -> List[dict]:
|
||||
return []
|
||||
|
||||
|
||||
def update(ref: str):
|
||||
return bool(system.run_cmd('flatpak update -y ' + ref))
|
||||
def update(app_ref: str) -> bool:
|
||||
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():
|
||||
|
||||
@@ -90,3 +90,19 @@ class FlatpakManager:
|
||||
return [*self.apps]
|
||||
|
||||
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
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
local_resource_path = '{}/.local/share/fpakman/resources'.format(str(Path.home()))
|
||||
app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def get_path(resource_path):
|
||||
|
||||
if os.path.exists(local_resource_path):
|
||||
return local_resource_path + '/' + resource_path
|
||||
else:
|
||||
return app_dir + '/resources/' + resource_path
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import subprocess
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -8,7 +8,7 @@ from PyQt5.QtGui import QIcon, QColor, QPixmap, QWindowStateChangeEvent
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QTableWidget, \
|
||||
QTableWidgetItem, QTableView, QCheckBox, QHeaderView, QToolButton, QToolBar, \
|
||||
QSizePolicy, QLabel, QMessageBox
|
||||
QSizePolicy, QLabel, QMessageBox, QPlainTextEdit
|
||||
|
||||
from fpakman.core import __version__
|
||||
from fpakman.core import resource
|
||||
@@ -94,7 +94,7 @@ class ManageWindow(QWidget):
|
||||
|
||||
self.bt_refresh = QToolButton()
|
||||
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)
|
||||
|
||||
self.bt_update = QToolButton()
|
||||
@@ -118,8 +118,16 @@ class ManageWindow(QWidget):
|
||||
|
||||
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.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.signal.connect(self._finish_refresh)
|
||||
@@ -202,11 +210,16 @@ class ManageWindow(QWidget):
|
||||
|
||||
self.thread_lock.release()
|
||||
|
||||
def refresh(self):
|
||||
def refresh(self, clear_output: bool = True):
|
||||
|
||||
if self._acquire_lock():
|
||||
self._check_flatpak_installed()
|
||||
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()
|
||||
|
||||
def _finish_refresh(self):
|
||||
@@ -248,6 +261,8 @@ class ManageWindow(QWidget):
|
||||
|
||||
self.bt_update.setEnabled(enable_bt_update)
|
||||
|
||||
self.tray_icon.notify_updates(updates)
|
||||
|
||||
def centralize(self):
|
||||
geo = self.frameGeometry()
|
||||
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
|
||||
@@ -338,21 +353,24 @@ class ManageWindow(QWidget):
|
||||
|
||||
if self._acquire_lock():
|
||||
if self.apps:
|
||||
|
||||
to_update = [pak['model']['ref'] for pak in self.apps if pak['visible'] and pak['update_checked']]
|
||||
|
||||
if to_update:
|
||||
self.textarea_output.clear()
|
||||
self.textarea_output.setVisible(True)
|
||||
|
||||
self._begin_action(self.locale_keys['manage_window.status.updating'] + '...')
|
||||
self.thread_update.refs_to_update = to_update
|
||||
self.thread_update.start()
|
||||
|
||||
def _finish_update_selected(self):
|
||||
self.update_apps(self.thread_update.updated_apps)
|
||||
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.refresh(clear_output=False)
|
||||
|
||||
def _update_action_output(self, output: str):
|
||||
self.textarea_output.appendPlainText(output)
|
||||
|
||||
def _begin_action(self, action_label: str):
|
||||
self.label_status.setText(action_label)
|
||||
@@ -369,20 +387,25 @@ class ManageWindow(QWidget):
|
||||
|
||||
|
||||
# Threaded actions
|
||||
|
||||
class UpdateSelectedApps(QThread):
|
||||
|
||||
signal = pyqtSignal()
|
||||
signal_finished = pyqtSignal()
|
||||
signal_output = pyqtSignal(str)
|
||||
|
||||
def __init__(self, controller: FlatpakController):
|
||||
super(UpdateSelectedApps, self).__init__()
|
||||
self.controller = controller
|
||||
self.refs_to_update = []
|
||||
self.updated_apps = None
|
||||
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user