screenshots button

This commit is contained in:
Vinicius Moreira
2019-10-10 12:37:43 -03:00
parent 64a23a11be
commit 6023075b65
19 changed files with 295 additions and 8 deletions

View File

@@ -247,3 +247,10 @@ class SoftwareManager(ABC):
@abstractmethod
def launch(self, pkg: SoftwarePackage):
pass
@abstractmethod
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
"""
:return: screenshot urls for the given package
"""
pass

View File

@@ -169,6 +169,12 @@ class SoftwarePackage(ABC):
"""
pass
def has_screenshots(self) -> bool:
"""
:return: if there are screenshots to be displayed
"""
return not self.installed
def __str__(self):
return '{} (id={}, name={})'.format(self.__class__.__name__, self.id, self.name)

View File

@@ -72,6 +72,7 @@ def main():
display_limit=args.max_displayed,
config=user_config,
context=context,
http_client=http_client,
notifications=bool(args.system_notifications))
if args.tray:

View File

@@ -364,3 +364,9 @@ class AppImageManager(SoftwareManager):
def cache_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
self.serialize_to_disk(pkg, icon_bytes, only_icon)
def get_screenshots(self, pkg: AppImage) -> List[str]:
if pkg.has_screenshots():
return [pkg.url_screenshot]
return []

View File

@@ -10,7 +10,7 @@ CACHED_ATTRS = {'name', 'description', 'version', 'url_download', 'author', 'lic
class AppImage(SoftwarePackage):
def __init__(self, name: str = None, description: str = None, github: str = None, source: str = None, version: str = None,
url_download: str = None, url_icon: str = None, license: str = None, author: str = None,
url_download: str = None, url_icon: str = None, url_screenshot: str = None, license: str = None, author: str = None,
pictures: List[str] = None, icon_path: str = None, installed: bool = False,
url_download_latest_version: str = None):
super(AppImage, self).__init__(id=name, name=name, version=version, latest_version=version,
@@ -21,6 +21,7 @@ class AppImage(SoftwarePackage):
self.pictures = pictures
self.url_download = url_download
self.icon_path = icon_path
self.url_screenshot = url_screenshot
self.author = author
self.url_download_latest_version = url_download_latest_version
@@ -80,3 +81,6 @@ class AppImage(SoftwarePackage):
def get_disk_icon_path(self):
return self.icon_path
def has_screenshots(self):
return not self.installed and self.url_screenshot

View File

@@ -1,4 +1,4 @@
APP_ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'license', 'author')
APP_ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'url_screenshot', 'license', 'author')
RELEASE_ATTRS = ('version', 'url_download', 'published_at')

View File

@@ -731,3 +731,6 @@ class ArchManager(SoftwareManager):
def launch(self, pkg: ArchPackage):
if pkg.command:
subprocess.Popen(pkg.command.split(' '))
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
pass

View File

@@ -272,4 +272,14 @@ class FlatpakManager(SoftwareManager):
return True
def launch(self, pkg: SoftwarePackage):
flatpak.run(pkg.id)
flatpak.run(str(pkg.id))
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
res = self.http_client.get_json('{}/apps/{}'.format(FLATHUB_API_URL, pkg.id))
urls = []
if res and res.get('screenshots'):
for s in res['screenshots']:
if s.get('imgDesktopUrl'):
urls.append(s['imgDesktopUrl'])
return urls

View File

@@ -191,3 +191,21 @@ class SnapManager(SoftwareManager):
def launch(self, pkg: SnapApplication):
snap.run(pkg, self.context.logger)
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
res = self.http_client.get_json('{}/search?q={}'.format(SNAP_API_URL, pkg.name))
if res:
if res.get('_embedded') and res['_embedded'].get('clickindex:package'):
snap_data = res['_embedded']['clickindex:package'][0]
if snap_data.get('screenshot_urls'):
return snap_data['screenshot_urls']
else:
self.logger.warning("No 'screenshots_urls' defined for {}".format(pkg))
else:
self.logger.error('It seems the API is returning a different response: {}'.format(res))
else:
self.logger.warning('Could not retrieve data for {}'.format(pkg))
return []

View File

@@ -369,3 +369,9 @@ class GenericSoftwareManager(SoftwareManager):
if man:
self.logger.info('Launching {}'.format(pkg))
man.launch(pkg)
def get_screenshots(self, pkg: SoftwarePackage):
man = self._get_manager_for(pkg)
if man:
return man.get_screenshots(pkg)

View File

@@ -417,6 +417,12 @@ class AppsTable(QTableWidget):
item.addWidget(IconButton(icon_path=resource.get_path('img/app_info.svg'), action=get_info, background='#2E68D3', tooltip=self.i18n['action.info.tooltip']))
if pkg.model.has_screenshots():
def get_screenshots():
self.window.get_screenshots(pkg)
item.addWidget(IconButton(icon_path=resource.get_path('img/camera.svg'), action=get_screenshots, background='purple', tooltip=self.i18n['action.screenshots.tooltip']))
def handle_click():
self.show_pkg_settings(pkg)

View File

@@ -0,0 +1,30 @@
from typing import List
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QDialog, QLabel, QVBoxLayout
from bauh.api.abstract.cache import MemoryCache
from bauh.view.qt.view_model import PackageView
class ScreenshotsDialog(QDialog):
def __init__(self, pkg: PackageView, icon_cache: MemoryCache, i18n: dict, screenshots: List[QPixmap]):
super(ScreenshotsDialog, self).__init__()
self.setWindowTitle(str(pkg))
icon_data = icon_cache.get(pkg.model.icon_url)
if icon_data and icon_data.get('icon'):
self.setWindowIcon(icon_data.get('icon'))
else:
self.setWindowIcon(QIcon(pkg.model.get_type_icon_path()))
self.setLayout(QVBoxLayout())
if screenshots:
lb = QLabel()
lb.setPixmap(screenshots[0])
self.layout().addWidget(lb)
self.adjustSize()

View File

@@ -5,6 +5,7 @@ from typing import List, Type, Set
import requests
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.controller import SoftwareManager
@@ -12,6 +13,7 @@ from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import PackageStatus, SoftwarePackage, PackageAction
from bauh.api.abstract.view import InputViewComponent, MessageType
from bauh.api.exception import NoInternetException
from bauh.api.http import HttpClient
from bauh.view.qt import commons
from bauh.view.qt.view_model import PackageView
@@ -480,3 +482,32 @@ class CustomAction(AsyncAction):
self.pkg = None
self.custom_action = None
self.root_password = None
class GetScreenshots(AsyncAction):
def __init__(self, manager: SoftwareManager, http_client: HttpClient, pkg: PackageView = None):
super(GetScreenshots, self).__init__()
self.pkg = pkg
self.manager = manager
self.http_client = http_client
def run(self):
if self.pkg:
imgs = []
screenshots = self.manager.get_screenshots(self.pkg.model)
if screenshots:
for url in screenshots:
res = self.http_client.get(url)
if res and res.status_code == 200:
pixmap = QPixmap()
pixmap.loadFromData(res.content)
imgs.append(pixmap)
break # TODO load all
self.notify_finished({'pkg': self.pkg, 'screenshots': imgs})
self.pkg = None

View File

@@ -14,8 +14,11 @@ from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.model import SoftwarePackage, PackageAction
from bauh.api.abstract.view import MessageType
from bauh.api.http import HttpClient
from bauh.commons.html import bold
from bauh.view.core.config import Configuration
from bauh.view.core.controller import GenericSoftwareManager
from bauh.view.qt.screenshots import ScreenshotsDialog
from bauh.view.util import util, resource
from bauh.view.qt import dialog, commons, qt_utils
from bauh.view.qt.about import AboutDialog
@@ -29,7 +32,7 @@ from bauh.view.qt.root import is_root, ask_root_password
from bauh.view.qt.styles import StylesComboBox
from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
GetAppHistory, SearchPackages, InstallPackage, AnimateProgress, VerifyModels, FindSuggestions, ListWarnings, \
AsyncAction, LaunchApp, ApplyFilters, CustomAction
AsyncAction, LaunchApp, ApplyFilters, CustomAction, GetScreenshots
from bauh.view.qt.view_model import PackageView
from bauh.view.qt.view_utils import load_icon
@@ -48,7 +51,7 @@ class ManageWindow(QWidget):
def __init__(self, i18n: dict, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool,
download_icons: bool, screen_size, suggestions: bool, display_limit: int, config: Configuration,
context: ApplicationContext, notifications: bool, tray_icon=None):
context: ApplicationContext, notifications: bool, http_client: HttpClient, tray_icon=None):
super(ManageWindow, self).__init__()
self.i18n = i18n
self.manager = manager
@@ -65,6 +68,7 @@ class ManageWindow(QWidget):
self.config = config
self.context = context
self.notifications = notifications
self.http_client = http_client
self.icon_app = QIcon(resource.get_path('img/logo.svg'))
self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
@@ -213,6 +217,7 @@ class ManageWindow(QWidget):
self.thread_suggestions = self._bind_async_action(FindSuggestions(man=self.manager), finished_call=self._finish_search, only_finished=True)
self.thread_run_app = self._bind_async_action(LaunchApp(self.manager), finished_call=self._finish_run_app, only_finished=False)
self.thread_custom_action = self._bind_async_action(CustomAction(manager=self.manager), finished_call=self._finish_custom_action)
self.thread_screenshots = self._bind_async_action(GetScreenshots(self.manager, http_client), finished_call=self._finish_get_screenshots)
self.thread_apply_filters = ApplyFilters()
self.thread_apply_filters.signal_finished.connect(self._finish_apply_filters_async)
@@ -811,6 +816,24 @@ class ManageWindow(QWidget):
self.thread_get_info.app = pkg
self.thread_get_info.start()
def get_screenshots(self, pkg: PackageView):
self._handle_console_option(False)
self._begin_action(self.i18n['manage_window.status.screenshots'].format(bold(pkg.model.name)))
self.thread_screenshots.pkg = pkg
self.thread_screenshots.start()
def _finish_get_screenshots(self, res: dict):
self.finish_action()
if res.get('screenshots'):
diag = ScreenshotsDialog(pkg=res['pkg'], icon_cache=self.icon_cache, i18n=self.i18n, screenshots=res['screenshots'])
diag.exec_()
else:
dialog.show_message(title=self.i18n['error'],
body=self.i18n['popup.screenshots.no_screenshot.body'].format(bold(res['pkg'].model.name)),
type_=MessageType.ERROR)
def get_app_history(self, app: dict):
self._handle_console_option(False)
self._begin_action(self.i18n['manage_window.status.history'])

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.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"
viewBox="0 0 47.999996 44"
xml:space="preserve"
sodipodi:docname="camera_2.svg"
width="47.999996"
height="44"
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
id="metadata945"><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="defs943">
</defs><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="namedview941"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.78647591"
inkscape:cx="-389.13442"
inkscape:cy="-153.68157"
inkscape:window-x="0"
inkscape:window-y="432"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g903"><path
style="fill:#ffffff;stroke-width:1.88976378;stroke:#ffffff;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path900"
d="M 0,13.46442 V 37.38396 C 0,41.03495 2.625306,44 5.857959,44 h 36.28408 c 3.232653,0 5.857959,-2.96505 5.857959,-6.61604 V 13.46442 c 0,-3.47398 -2.497959,-6.2952 -5.573877,-6.2952 H 34.62857 L 34.442448,6.25094 C 33.688162,2.56676 30.817958,0 27.467754,0 H 20.522448 C 17.18204,0 14.311836,2.56676 13.547755,6.25094 L 13.361632,7.16922 H 5.5738773 C 2.4979591,7.16922 0,10.00151 0,13.46442 Z M 14.321632,9.87981 c 0.558367,0 1.038367,-0.43148 1.165714,-1.05105 l 0.401633,-1.96932 c 0.509388,-2.44506 2.409796,-4.14886 4.633469,-4.14886 h 6.945306 c 2.223673,0 4.124082,1.7038 4.633469,4.14886 l 0.401633,1.96932 c 0.127347,0.6085 0.607347,1.05105 1.165714,1.05105 h 8.747755 c 1.753469,0 3.173877,1.60422 3.173877,3.58461 v 23.91954 c 0,2.1574 -1.547755,3.90545 -3.457959,3.90545 H 5.857959 c -1.910204,0 -3.4579591,-1.74805 -3.4579591,-3.90545 V 13.46442 c 0,-1.98039 1.4204081,-3.58461 3.1738774,-3.58461 z" /><path
style="fill:#ffffff;stroke-width:1.88976378;stroke:#ffffff;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path904"
d="m 23.999999,37.04099 c 5.554286,0 10.08,-5.1114 10.08,-11.38447 0,-6.27307 -4.525714,-11.38446 -10.08,-11.38446 -5.554285,0 -10.08,5.10033 -10.08,11.38446 0,6.28414 4.525715,11.38447 10.08,11.38447 z m 0,-20.05834 c 4.231837,0 7.68,3.89439 7.68,8.67387 0,4.77949 -3.448163,8.67388 -7.68,8.67388 -4.231836,0 -7.68,-3.89439 -7.68,-8.67388 0,-4.77948 3.448164,-8.67387 7.68,-8.67387 z" /></g>
<g
id="g910"
transform="translate(0,-410.99999)">
</g>
<g
id="g912"
transform="translate(0,-410.99999)">
</g>
<g
id="g914"
transform="translate(0,-410.99999)">
</g>
<g
id="g916"
transform="translate(0,-410.99999)">
</g>
<g
id="g918"
transform="translate(0,-410.99999)">
</g>
<g
id="g920"
transform="translate(0,-410.99999)">
</g>
<g
id="g922"
transform="translate(0,-410.99999)">
</g>
<g
id="g924"
transform="translate(0,-410.99999)">
</g>
<g
id="g926"
transform="translate(0,-410.99999)">
</g>
<g
id="g928"
transform="translate(0,-410.99999)">
</g>
<g
id="g930"
transform="translate(0,-410.99999)">
</g>
<g
id="g932"
transform="translate(0,-410.99999)">
</g>
<g
id="g934"
transform="translate(0,-410.99999)">
</g>
<g
id="g936"
transform="translate(0,-410.99999)">
</g>
<g
id="g938"
transform="translate(0,-410.99999)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -27,6 +27,7 @@ manage_window.status.installing=Installing
manage_window.status.refreshing_app=Refreshing
manage_window.status.running_app=Launching {}
manage_window.status.filtering=Filtering
manage_window.status.screenshots=Retrieving {} screenshots
manage_window.bt.refresh.text=Refresh
manage_window.bt.refresh.tooltip=Reload the data about installed applications
manage_window.bt.upgrade.text=Upgrade
@@ -47,6 +48,7 @@ tray.action.exit=Quit
tray.action.about=About
tray.action.refreshing=Refreshing
action.info.tooltip=Click here to see information about this application
action.screenshots.tooltip=Click here to see some pictures of this application
action.settings.tooltip=Click here to open extra actions
notification.update={} available update
notification.updates={} available updates
@@ -121,4 +123,5 @@ author=author
source=source
size=size
categories=categories
summary=summary
summary=summary
popup.screenshots.no_screenshot.body=it was not possible to retrieve {} screenshots

View File

@@ -123,4 +123,7 @@ author=autor
source=origen
size=tamaño
categories=categorías
summary=sumario
summary=sumario
manage_window.status.screenshots=Obteniendo imágenes de {}
action.screenshots.tooltip=Haga clic aquí para ver algunas fotos de esta aplicación
popup.screenshots.no_screenshot.body=No fue posible recuperar las fotos de {}

View File

@@ -123,4 +123,7 @@ author=autor
source=fonte
size=tamanho
categories=categorias
summary=resumo
summary=resumo
manage_window.status.screenshots=Obtendo imagens de {}
action.screenshots.tooltip=Clique aqui para ver algumas fotos desse aplicativo
popup.screenshots.no_screenshot.body=Não foi possível obter as fotos de {}