mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 09:24:16 +02:00
suggestions button implemented respecting already installed applications
This commit is contained in:
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- bauh relies on [NodeJS](https://nodejs.org/en/), [Electron](https://electronjs.org/) and [nativefier](https://github.com/jiahaog/nativefier) to install the Web applications, but there is no need to have them installed on your system. Bauh will create its own installation environment with these technologies in **~/.local/share/bauh/web/env**.
|
||||
- suggestions are retrieved from [suggestions.txt](https://github.com/vinifmor/bauh-files/blob/master/web/suggestions.yml)
|
||||
- requires only **python-beautifulsoup4** and **python-lxml** to be enabled
|
||||
- **Suggestions** button: it allows retrieving the application suggestions any time
|
||||
|
||||
### Improvements
|
||||
- configuration file **~/.config/bauh/config.json** renamed to **~/.config/bauh/config.yml**
|
||||
|
||||
@@ -17,7 +17,7 @@ class HttpClient:
|
||||
self.sleep = sleep
|
||||
self.logger = logger
|
||||
|
||||
def get(self, url: str, params: dict = None, headers: dict = None, allow_redirects: bool = True, ignore_ssl: bool = False, single_call: bool = False):
|
||||
def get(self, url: str, params: dict = None, headers: dict = None, allow_redirects: bool = True, ignore_ssl: bool = False, single_call: bool = False) -> requests.Request:
|
||||
cur_attempts = 1
|
||||
|
||||
while cur_attempts <= self.max_attempts:
|
||||
|
||||
@@ -26,6 +26,7 @@ from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, r
|
||||
from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, SUGGESTIONS_FILE
|
||||
from bauh.gems.appimage.config import read_config
|
||||
from bauh.gems.appimage.model import AppImage
|
||||
from bauh.gems.appimage.query import FIND_APPS_BY_NAME, FIND_APPS_BY_NAME_ONLY_NAME
|
||||
from bauh.gems.appimage.worker import DatabaseUpdater
|
||||
|
||||
DB_APPS_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/apps.db')
|
||||
@@ -104,7 +105,8 @@ class AppImageManager(SoftwareManager):
|
||||
res.total = len(res.installed) + len(res.new)
|
||||
return res
|
||||
|
||||
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult:
|
||||
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False,
|
||||
pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None, connection: sqlite3.Connection = None) -> SearchResult:
|
||||
res = SearchResult([], [], 0)
|
||||
|
||||
if os.path.exists(INSTALLATION_PATH):
|
||||
@@ -122,7 +124,7 @@ class AppImageManager(SoftwareManager):
|
||||
names.add("'{}'".format(app.name.lower()))
|
||||
|
||||
if res.installed:
|
||||
con = self._get_db_connection(DB_APPS_PATH)
|
||||
con = self._get_db_connection(DB_APPS_PATH) if not connection else connection
|
||||
|
||||
if con:
|
||||
try:
|
||||
@@ -142,7 +144,8 @@ class AppImageManager(SoftwareManager):
|
||||
except:
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
self._close_connection(DB_APPS_PATH, con)
|
||||
if not connection:
|
||||
self._close_connection(DB_APPS_PATH, con)
|
||||
|
||||
res.total = len(res.installed)
|
||||
return res
|
||||
@@ -432,14 +435,23 @@ class AppImageManager(SoftwareManager):
|
||||
try:
|
||||
sugs = [l for l in file.text.split('\n') if l]
|
||||
|
||||
if 0 < limit < len(sugs):
|
||||
sugs = sugs[0:limit]
|
||||
if filter_installed:
|
||||
installed = {i.name.lower() for i in self.read_installed(disk_loader=None, connection=connection).installed}
|
||||
else:
|
||||
installed = None
|
||||
|
||||
sugs_map = {}
|
||||
|
||||
for s in sugs:
|
||||
lsplit = s.split('=')
|
||||
sugs_map[lsplit[1].strip()] = SuggestionPriority(int(lsplit[0]))
|
||||
|
||||
name = lsplit[1].strip()
|
||||
|
||||
if limit <= 0 or len(sugs_map) < limit:
|
||||
if not installed or not name.lower() in installed:
|
||||
sugs_map[name] = SuggestionPriority(int(lsplit[0]))
|
||||
else:
|
||||
break
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(query.FIND_APPS_BY_NAME_FULL.format(','.join(["'{}'".format(s) for s in sugs_map.keys()])))
|
||||
|
||||
@@ -5,5 +5,6 @@ RELEASE_ATTRS = ('version', 'url_download', 'published_at')
|
||||
SEARCH_APPS_BY_NAME_OR_DESCRIPTION = "SELECT {} FROM apps".format(','.join(APP_ATTRS)) + " WHERE lower(name) LIKE '%{}%' or lower(description) LIKE '%{}%'"
|
||||
FIND_APP_ID_BY_NAME_AND_GITHUB = "SELECT id FROM apps WHERE lower(name) = '{}' and lower(github) = '{}'"
|
||||
FIND_APPS_BY_NAME = "SELECT name, github, version, url_download FROM apps WHERE lower(name) IN ({})"
|
||||
FIND_APPS_BY_NAME_ONLY_NAME = "SELECT name FROM apps WHERE lower(name) IN ({})"
|
||||
FIND_APPS_BY_NAME_FULL = "SELECT {} FROM apps".format(','.join(APP_ATTRS)) + " WHERE lower(name) IN ({})"
|
||||
FIND_RELEASES_BY_APP_ID = "SELECT {} FROM releases".format(','.join(RELEASE_ATTRS)) + " WHERE app_id = {} ORDER BY version desc"
|
||||
|
||||
@@ -848,7 +848,10 @@ class ArchManager(SoftwareManager):
|
||||
if l:
|
||||
if limit <= 0 or len(suggestions) < limit:
|
||||
lsplit = l.split('=')
|
||||
suggestions[lsplit[1].strip()] = SuggestionPriority(int(lsplit[0]))
|
||||
name = lsplit[1].strip()
|
||||
|
||||
if not filter_installed or not pacman.check_installed(name):
|
||||
suggestions[name] = SuggestionPriority(int(lsplit[0]))
|
||||
|
||||
api_res = self.aur_client.get_info(suggestions.keys())
|
||||
|
||||
|
||||
@@ -147,7 +147,12 @@ class FlatpakManager(SoftwareManager):
|
||||
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.update(pkg.ref)))
|
||||
|
||||
def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref)))
|
||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.uninstall(pkg.ref)))
|
||||
|
||||
if self.suggestions_cache:
|
||||
self.suggestions_cache.delete(pkg.id)
|
||||
|
||||
return uninstalled
|
||||
|
||||
def get_info(self, app: FlatpakApplication) -> dict:
|
||||
if app.installed:
|
||||
@@ -272,12 +277,19 @@ class FlatpakManager(SoftwareManager):
|
||||
return res
|
||||
else:
|
||||
self.logger.info("Mapping suggestions")
|
||||
installed = {i.id for i in self.read_installed(disk_loader=None).installed} if filter_installed else None
|
||||
|
||||
for line in file.text.split('\n'):
|
||||
if line:
|
||||
if limit <= 0 or len(res) < limit:
|
||||
sug = line.split('=')
|
||||
appid, priority = sug[1], SuggestionPriority(int(sug[0]))
|
||||
appid = sug[1].strip()
|
||||
|
||||
if installed and appid in installed:
|
||||
continue
|
||||
|
||||
priority = SuggestionPriority(int(sug[0]))
|
||||
|
||||
cached_sug = self.suggestions_cache.get(appid)
|
||||
|
||||
if cached_sug:
|
||||
|
||||
@@ -8,6 +8,7 @@ from bauh.api.exception import NoInternetException
|
||||
from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess
|
||||
|
||||
BASE_CMD = 'flatpak'
|
||||
RE_SEVERAL_SPACES = re.compile(r'\s+')
|
||||
|
||||
|
||||
def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_runtime: bool = False):
|
||||
|
||||
@@ -126,7 +126,12 @@ class SnapManager(SoftwareManager):
|
||||
raise Exception("'update' is not supported by {}".format(pkg.__class__.__name__))
|
||||
|
||||
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
return ProcessHandler(watcher).handle(SystemProcess(subproc=snap.uninstall_and_stream(pkg.name, root_password)))
|
||||
uninstalled = ProcessHandler(watcher).handle(SystemProcess(subproc=snap.uninstall_and_stream(pkg.name, root_password)))
|
||||
|
||||
if self.suggestions_cache:
|
||||
self.suggestions_cache.delete(pkg.name)
|
||||
|
||||
return uninstalled
|
||||
|
||||
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
|
||||
return {SnapApplication}
|
||||
@@ -250,20 +255,24 @@ class SnapManager(SoftwareManager):
|
||||
self.categories_downloader.join()
|
||||
|
||||
suggestions, threads = [], []
|
||||
installed = {i.name.lower() for i in self.read_installed(disk_loader=None).installed} if filter_installed else None
|
||||
|
||||
for l in file.text.split('\n'):
|
||||
if l:
|
||||
if limit <= 0 or len(suggestions) < limit:
|
||||
sug = l.strip().split('=')
|
||||
cached_sug = self.suggestions_cache.get(sug[1])
|
||||
name = sug[1]
|
||||
|
||||
if cached_sug:
|
||||
res.append(cached_sug)
|
||||
else:
|
||||
t = Thread(target=self._fill_suggestion, args=(sug[1], SuggestionPriority(int(sug[0])), res))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
time.sleep(0.001) # to avoid being blocked
|
||||
if not installed or name not in installed:
|
||||
cached_sug = self.suggestions_cache.get(name)
|
||||
|
||||
if cached_sug:
|
||||
res.append(cached_sug)
|
||||
else:
|
||||
t = Thread(target=self._fill_suggestion, args=(name, SuggestionPriority(int(sug[0])), res))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
time.sleep(0.001) # to avoid being blocked
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
@@ -782,6 +782,17 @@ class WebApplicationManager(SoftwareManager):
|
||||
if suggestions:
|
||||
suggestion_list = list(suggestions.values())
|
||||
suggestion_list.sort(key=lambda s: s.get('priority', 0), reverse=True)
|
||||
|
||||
if filter_installed:
|
||||
installed = self.read_installed(disk_loader=None)
|
||||
url_map = {self._strip_url_protocol(s['url']): idx for idx, s in enumerate(suggestion_list)}
|
||||
|
||||
if installed.installed:
|
||||
for i in installed.installed:
|
||||
s_url = self._strip_url_protocol(i.url)
|
||||
if s_url in url_map:
|
||||
suggestion_list.pop(url_map[s_url])
|
||||
|
||||
to_map = suggestion_list if limit <= 0 else suggestion_list[0:limit]
|
||||
res = [self._map_suggestion(s) for s in to_map]
|
||||
|
||||
|
||||
@@ -328,10 +328,10 @@ class GenericSoftwareManager(SoftwareManager):
|
||||
|
||||
return warnings
|
||||
|
||||
def _fill_suggestions(self, suggestions: list, man: SoftwareManager, limit: int):
|
||||
def _fill_suggestions(self, suggestions: list, man: SoftwareManager, limit: int, filter_installed: bool):
|
||||
if self._can_work(man):
|
||||
mti = time.time()
|
||||
man_sugs = man.list_suggestions(limit)
|
||||
man_sugs = man.list_suggestions(limit=limit, filter_installed=filter_installed)
|
||||
mtf = time.time()
|
||||
self.logger.info(man.__class__.__name__ + ' took {0:.2f} seconds'.format(mtf - mti))
|
||||
|
||||
@@ -346,7 +346,7 @@ class GenericSoftwareManager(SoftwareManager):
|
||||
if self.managers and internet.is_available(self.context.http_client, self.context.logger):
|
||||
suggestions, threads = [], []
|
||||
for man in self.managers:
|
||||
t = Thread(target=self._fill_suggestions, args=(suggestions, man, int(self.config['suggestions']['by_type'])))
|
||||
t = Thread(target=self._fill_suggestions, args=(suggestions, man, int(self.config['suggestions']['by_type']), filter_installed))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
|
||||
@@ -393,9 +393,10 @@ class FindSuggestions(AsyncAction):
|
||||
def __init__(self, man: SoftwareManager):
|
||||
super(FindSuggestions, self).__init__()
|
||||
self.man = man
|
||||
self.filter_installed = False
|
||||
|
||||
def run(self):
|
||||
sugs = self.man.list_suggestions(limit=-1)
|
||||
sugs = self.man.list_suggestions(limit=-1, filter_installed=self.filter_installed)
|
||||
self.notify_finished({'pkgs_found': [s.package for s in sugs] if sugs is not None else [], 'error': None})
|
||||
|
||||
|
||||
|
||||
@@ -189,6 +189,17 @@ class ManageWindow(QWidget):
|
||||
self.bt_installed.setStyleSheet(toolbar_button_style('#A94E0A'))
|
||||
self.ref_bt_installed = self.toolbar.addWidget(self.bt_installed)
|
||||
|
||||
if config['suggestions']['enabled']:
|
||||
self.bt_suggestions = QPushButton()
|
||||
self.bt_suggestions.setText('Suggestions')
|
||||
self.bt_suggestions.setIcon(QIcon(resource.get_path('img/suggestions.svg')))
|
||||
self.bt_suggestions.setStyleSheet(toolbar_button_style('#FF8000'))
|
||||
self.bt_suggestions.clicked.connect(self.read_suggestions)
|
||||
self.ref_bt_suggestions = self.toolbar.addWidget(self.bt_suggestions)
|
||||
else:
|
||||
self.bt_suggestions = None
|
||||
self.ref_bt_suggestions = None
|
||||
|
||||
self.bt_refresh = QPushButton()
|
||||
self.bt_refresh.setToolTip(i18n['manage_window.bt.refresh.tooltip'])
|
||||
self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
|
||||
@@ -492,6 +503,15 @@ class ManageWindow(QWidget):
|
||||
self.thread_refresh.pkg_types = pkg_types
|
||||
self.thread_refresh.start()
|
||||
|
||||
def read_suggestions(self):
|
||||
self.input_search.clear()
|
||||
self._handle_console_option(False)
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
self.ref_checkbox_only_apps.setVisible(False)
|
||||
self._begin_action('Retrieving suggestions', keep_bt_installed=False, clear_filters=not self.recent_uninstall)
|
||||
self.thread_suggestions.filter_installed = True
|
||||
self.thread_suggestions.start()
|
||||
|
||||
def _finish_refresh_apps(self, res: dict, as_installed: bool = True):
|
||||
self.finish_action()
|
||||
self.ref_checkbox_only_apps.setVisible(bool(res['installed']))
|
||||
@@ -671,6 +691,7 @@ class ManageWindow(QWidget):
|
||||
if pkgs_info['apps_count'] == 0:
|
||||
if self.first_refresh or self.types_changed:
|
||||
self._begin_search('')
|
||||
self.thread_suggestions.filter_installed = False
|
||||
self.thread_suggestions.start()
|
||||
return
|
||||
else:
|
||||
@@ -870,6 +891,10 @@ class ManageWindow(QWidget):
|
||||
self.label_status.setText(action_label + "...")
|
||||
self.ref_bt_upgrade.setVisible(False)
|
||||
self.ref_bt_refresh.setVisible(False)
|
||||
|
||||
if self.ref_bt_suggestions:
|
||||
self.ref_bt_suggestions.setVisible(False)
|
||||
|
||||
self.checkbox_only_apps.setEnabled(False)
|
||||
self.table_apps.setEnabled(False)
|
||||
self.checkbox_updates.setEnabled(False)
|
||||
@@ -913,6 +938,9 @@ class ManageWindow(QWidget):
|
||||
self.checkbox_updates.setEnabled(True)
|
||||
self.progress_controll_enabled = True
|
||||
|
||||
if self.ref_bt_suggestions:
|
||||
self.ref_bt_suggestions.setVisible(True)
|
||||
|
||||
if self.pkgs:
|
||||
self.ref_input_name_filter.setVisible(True)
|
||||
self.update_bt_upgrade()
|
||||
@@ -1113,10 +1141,6 @@ class ManageWindow(QWidget):
|
||||
def _show_settings_menu(self):
|
||||
menu_row = QMenu()
|
||||
|
||||
# TODO
|
||||
# action_suggestions = QAction(self.i18n['manage_window.settings.suggestions'])
|
||||
# action_suggestions.setIcon(QIcon())
|
||||
|
||||
if isinstance(self.manager, GenericSoftwareManager):
|
||||
action_gems = QAction(self.i18n['manage_window.settings.gems'])
|
||||
action_gems.setIcon(self.icon_app)
|
||||
|
||||
168
bauh/view/resources/img/suggestions.svg
Normal file
168
bauh/view/resources/img/suggestions.svg
Normal file
@@ -0,0 +1,168 @@
|
||||
<?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 48.000001 48.000001"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="suggestions.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
width="48"
|
||||
height="48"><metadata
|
||||
id="metadata3871"><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="defs3869" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="703"
|
||||
id="namedview3867"
|
||||
showgrid="false"
|
||||
showborder="false"
|
||||
inkscape:zoom="1.1933395"
|
||||
inkscape:cx="193.79578"
|
||||
inkscape:cy="-23.859998"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<g
|
||||
id="g3834"
|
||||
transform="matrix(0.13315982,0,0,0.11100833,-4.7891638,0)"
|
||||
style="fill:#ffffff">
|
||||
<g
|
||||
id="g3832"
|
||||
style="fill:#ffffff">
|
||||
<g
|
||||
id="g3830"
|
||||
style="fill:#ffffff">
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 216.529,93.2 c -61.2,0 -111.2,50 -111.2,111.2 0,32 14,62.8 37.6,83.6 17.6,17.6 16,55.2 15.6,55.6 0,2 0.4,3.6 2,5.2 1.2,1.2 3.2,2 4.8,2 h 102 c 2,0 3.6,-0.8 4.8,-2 1.2,-1.2 2,-3.2 2,-5.2 0,-0.4 -2,-38 15.6,-55.6 0.4,-0.4 0.8,-0.8 1.2,-1.2 23.2,-21.2 36.8,-51.2 36.8,-82.4 0,-61.2 -50,-111.2 -111.2,-111.2 z m 64,184.4 c -0.4,0.4 -1.2,1.2 -1.2,1.6 -15.6,16.8 -18.4,44.4 -18.8,57.6 h -88.4 c -0.4,-13.2 -3.2,-42 -20,-59.2 -21.2,-18.4 -33.6,-45.2 -33.6,-73.6 0,-54 43.6,-97.6 97.6,-97.6 54,0 97.6,43.6 97.6,97.6 0,28.4 -12,55.2 -33.2,73.6 z"
|
||||
id="path3812"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 216.129,121.6 c -3.6,0 -6.8,3.2 -6.8,6.8 0,3.6 3.2,6.8 6.8,6.8 40.4,0 72.8,32.8 72.8,72.8 0,3.6 3.2,6.8 6.8,6.8 3.6,0 6.8,-3.2 6.8,-6.8 0.4,-47.6 -38.4,-86.4 -86.4,-86.4 z"
|
||||
id="path3814"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 260.529,358.4 h -88.8 c -9.2,0 -16.8,7.6 -16.8,16.8 0,9.2 7.6,16.8 16.8,16.8 h 88.4 c 9.6,-0.4 17.2,-7.6 17.2,-16.8 0,-9.2 -7.6,-16.8 -16.8,-16.8 z m 0,19.6 h -88.8 c -1.6,0 -3.2,-1.2 -3.2,-3.2 0,-2 1.2,-3.2 3.2,-3.2 h 88.4 c 1.6,0 3.2,1.2 3.2,3.2 0,2 -1.2,3.2 -2.8,3.2 z"
|
||||
id="path3816"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 247.329,398.8 h -62.4 c -9.2,0 -16.8,7.6 -16.8,16.8 0,9.2 7.6,16.8 16.8,16.8 h 62.4 c 9.2,0 16.8,-7.6 16.8,-16.8 0,-9.6 -7.6,-16.8 -16.8,-16.8 z m 0,19.6 h -62.4 c -1.6,0 -3.2,-1.2 -3.2,-3.2 0,-2 1.2,-3.2 3.2,-3.2 h 62.4 c 1.6,0 3.2,1.2 3.2,3.2 0,2 -1.6,3.2 -3.2,3.2 z"
|
||||
id="path3818"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 216.129,60 c 4,0 6.8,-3.2 6.8,-6.8 V 6.8 c 0,-3.6 -3.2,-6.8 -6.8,-6.8 -3.6,0 -6.8,3.2 -6.8,6.8 v 46.4 c 0,3.6 3.2,6.8 6.8,6.8 z"
|
||||
id="path3820"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 329.329,34.4 c -3.2,-2.4 -7.2,-1.2 -9.2,1.6 l -25.6,38.4 c -2.4,3.2 -1.6,7.6 1.6,9.6 1.2,0.8 2.4,1.2 3.6,1.2 2.4,0 4.4,-1.2 5.6,-3.2 l 25.6,-38.4 c 2.4,-2.8 1.6,-7.2 -1.6,-9.2 z"
|
||||
id="path3822"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 134.929,83.6 c 1.2,0 2.4,-0.4 3.6,-1.2 3.2,-2 4,-6.4 2,-9.6 L 115.729,34 c -2,-3.2 -6.4,-4 -9.6,-2 -3.2,2 -4,6.4 -2,9.6 l 24.8,38.8 c 1.6,2.4 3.6,3.2 6,3.2 z"
|
||||
id="path3824"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 86.529,126 -40.4,-22 c -3.2,-1.6 -7.6,-0.4 -9.2,2.8 -2,3.2 -0.8,7.6 2.8,9.2 l 40.4,22 c 1.2,0.4 2,0.8 3.2,0.8 2.4,0 4.8,-1.2 6,-3.6 1.6,-3.2 0.4,-7.6 -2.8,-9.2 z"
|
||||
id="path3826"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 395.729,106.8 c -1.6,-3.2 -6,-4.4 -9.2,-2.8 l -40.8,22 c -3.2,1.6 -4.4,6 -2.8,9.2 1.2,2.4 3.6,3.6 6,3.6 1.2,0 2.4,-0.4 3.2,-0.8 l 40.8,-22 c 3.2,-1.6 4.4,-6 2.8,-9.2 z"
|
||||
id="path3828"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g3836"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3838"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3840"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3842"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3844"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3846"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3848"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3850"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3852"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3854"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3856"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3858"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3860"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3862"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
<g
|
||||
id="g3864"
|
||||
transform="translate(-35.965532,-384.4)">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
Reference in New Issue
Block a user