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,7 +6,7 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QHeaderView
from fpakman.core import resource
from fpakman.core import resource, util
from fpakman.view.qt import dialog
@@ -44,8 +44,9 @@ class AppsTable(QTableWidget):
'manage_window.columns.latest_version',
'flatpak.info.branch',
'flatpak.info.arch',
'flatpak.info.ref',
'flatpak.info.id',
'flatpak.info.origin',
'manage_window.columns.installed',
'manage_window.columns.update']]
self.setColumnCount(len(self.column_names))
self.setFocusPolicy(Qt.NoFocus)
@@ -62,33 +63,40 @@ class AppsTable(QTableWidget):
self.icon_cache = {}
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()
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)
menu_row = QMenu()
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)
if app['model']['installed']:
napps = len([app for app in self.window.apps if not app['model']['runtime']])
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)
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.popup(QCursor.pos())
@@ -122,6 +130,9 @@ class AppsTable(QTableWidget):
def _get_app_history(self):
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):
icon_url = http_response.url().toString()
pixmap = QPixmap()
@@ -142,9 +153,12 @@ class AppsTable(QTableWidget):
if 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.setText(app['model']['name'])
col_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_name.setToolTip(tooltip)
if not app['model']['icon']:
col_name.setIcon(self.icon_flathub)
@@ -162,11 +176,13 @@ class AppsTable(QTableWidget):
col_version = QTableWidgetItem()
col_version.setText(app['model']['version'])
col_version.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_version.setToolTip(tooltip)
self.setItem(idx, 1, col_version)
col_release = QTableWidgetItem()
col_release.setText(app['model']['latest_version'])
col_release.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_release.setToolTip(tooltip)
self.setItem(idx, 2, col_release)
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.setText(app['model']['branch'])
col_branch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_branch.setToolTip(tooltip)
self.setItem(idx, 3, col_branch)
col_arch = QTableWidgetItem()
col_arch.setText(app['model']['arch'])
col_arch.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_arch.setToolTip(tooltip)
self.setItem(idx, 4, col_arch)
col_package = QTableWidgetItem()
col_package.setText(app['model']['ref'])
col_package.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.setItem(idx, 5, col_package)
col_id = QTableWidgetItem()
col_id.setText(app['model']['id'])
col_id.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_id.setToolTip(tooltip)
self.setItem(idx, 5, col_id)
col_origin = QTableWidgetItem()
col_origin.setText(app['model']['origin'])
col_origin.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col_origin.setToolTip(tooltip)
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
self.setCellWidget(idx, 7, col_update)
self.setCellWidget(idx, 8, col_update)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
header_horizontal = self.horizontalHeader()

View File

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

View File

@@ -6,7 +6,7 @@ from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
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.window import ManageWindow
@@ -33,6 +33,19 @@ class UpdateCheck(QThread):
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):
def __init__(self, locale_keys: dict, manager: FlatpakManager, check_interval: int = 60):
@@ -46,8 +59,12 @@ class TrayIcon(QSystemTrayIcon):
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.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.triggered.connect(self.show_about)
@@ -64,6 +81,16 @@ class TrayIcon(QSystemTrayIcon):
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):
if updates > 0:
if self.icon().cacheKey() != self.icon_update.cacheKey():
@@ -72,11 +99,7 @@ class TrayIcon(QSystemTrayIcon):
msg = '{}: {}'.format(self.locale_keys['notification.new_updates'], updates)
self.setToolTip(msg)
if bool(os.getenv('FPAKMAN_UPDATE_NOTIFICATION', 1)):
os.system("notify-send -i {} '{}'".format(resource.get_path('img/flathub_45.svg'), msg))
if self.manage_window:
self.manage_window.refresh()
system.notify_user(msg)
else:
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['name'] = self.app['model']['name']
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.app = None
@@ -113,3 +114,42 @@ class GetAppHistory(QThread):
commits = flatpak.get_app_commits_data(self.app['model']['ref'], self.app['model']['origin'])
self.signal_finished.emit({'model': self.app['model'], 'commits': commits})
self.app = None
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 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, \
QSizePolicy, QLabel, QPlainTextEdit
QSizePolicy, QLabel, QPlainTextEdit, QLineEdit
from fpakman.core import resource, flatpak
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.root import is_root, ask_root_password
from fpakman.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
GetAppHistory
GetAppHistory, SearchApps, InstallApp
class ManageWindow(QWidget):
@@ -42,12 +42,43 @@ class ManageWindow(QWidget):
self.layout = QVBoxLayout()
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.setText(self.locale_keys['manage_window.checkbox.only_apps'])
self.checkbox_only_apps.setChecked(True)
self.checkbox_only_apps.stateChanged.connect(self.filter_only_apps)
toolbar = QToolBar()
toolbar.addWidget(self.checkbox_only_apps)
spacer = QWidget()
@@ -56,7 +87,7 @@ class ManageWindow(QWidget):
self.label_status = QLabel()
self.label_status.setText('')
self.label_status.setStyleSheet("color: orange")
self.label_status.setStyleSheet("color: orange; font-weight: bold")
toolbar.addWidget(self.label_status)
spacer = QWidget()
@@ -111,6 +142,13 @@ class ManageWindow(QWidget):
self.thread_get_history = GetAppHistory()
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.label_updates = QLabel('')
self.label_updates.setStyleSheet("color: red;")
@@ -170,6 +208,14 @@ class ManageWindow(QWidget):
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):
if self._acquire_lock():
@@ -177,8 +223,7 @@ class ManageWindow(QWidget):
self._begin_action(self.locale_keys['manage_window.status.refreshing'])
if clear_output:
self.textarea_output.clear()
self.textarea_output.hide()
self._hide_output()
self.thread_refresh.start()
@@ -321,17 +366,23 @@ class ManageWindow(QWidget):
def _begin_action(self, action_label: str):
self.label_status.setText(action_label + "...")
self.toolbar_search.setVisible(False)
self.bt_upgrade.setEnabled(False)
self.bt_refresh.setEnabled(False)
self.checkbox_only_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.toolbar_search.setVisible(True)
self.checkbox_only_apps.setEnabled(True)
self.table_apps.setEnabled(True)
self.input_search.setEnabled(True)
self.label_status.setText('')
if clear_search:
self.input_search.setText('')
def downgrade_app(self, app: dict):
self._check_flatpak_installed()
@@ -387,3 +438,37 @@ class ManageWindow(QWidget):
self.change_update_state()
dialog_history = HistoryDialog(app, self.table_apps.get_selected_app_icon(), self.locale_keys)
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)