Add the option to search a better icon on the html page
parent
33ebb4bdd4
commit
4c9b7da927
|
@ -1,2 +1,17 @@
|
|||
*Mon Apr 27 2015 Tobi Sim 0.0.1-1
|
||||
-initial commit
|
||||
*Mon Feb 13 2023 kirbylife <hola@kirbylife.dev> 0.5.0-7
|
||||
- Add SVG support
|
||||
- Improve performance
|
||||
- Add resize slider
|
||||
- Fix irregluar size of icons on the main grid
|
||||
- Add the option to search a better icon directly on the html page
|
||||
- Improve a bit the logging
|
||||
|
||||
*Mon Feb 13 2023 kirbylife <hola@kirbylife.dev> 0.4.5-5
|
||||
- Add aarch64 support
|
||||
- Add logging file
|
||||
- Skip the sailjail permissions
|
||||
- Remove all the third-repo dependencies
|
||||
- Now, when you re-edit an icon, the original icon is used instead of the edited one
|
||||
|
||||
*Mon May 17 2021 kirbylife <hola@kirbylife.dev> 0.4.0-1
|
||||
- First published version
|
||||
|
|
214
qml/Icon.qml
214
qml/Icon.qml
|
@ -1,6 +1,7 @@
|
|||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
import io.thp.pyotherside 1.3
|
||||
import Nemo.Notifications 1.0
|
||||
|
||||
Page {
|
||||
id: iconEditor
|
||||
|
@ -8,44 +9,106 @@ Page {
|
|||
property var attrs
|
||||
property var corners: [true, true, true, true]
|
||||
|
||||
PageHeader {
|
||||
id: header
|
||||
width: parent.width
|
||||
title: "name: " + attrs.Name
|
||||
Notification {
|
||||
id: notification
|
||||
appIcon: "/usr/share/icons/hicolor/86x86/apps/harbour-muchkin"
|
||||
expireTimeout: 5000
|
||||
|
||||
function sendMessage(msg) {
|
||||
notification.previewBody = msg
|
||||
notification.publish()
|
||||
}
|
||||
}
|
||||
|
||||
SilicaGridView {
|
||||
anchors.top: header.bottom
|
||||
width: parent.width
|
||||
height: width
|
||||
cellWidth: width / 2
|
||||
cellHeight: cellWidth
|
||||
SilicaFlickable {
|
||||
id: mainList
|
||||
anchors.fill: parent
|
||||
|
||||
model: ListModel {
|
||||
id: gridItems
|
||||
|
||||
ListElement {
|
||||
idCell: 0
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 1
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 2
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 3
|
||||
activated: true
|
||||
PullDownMenu {
|
||||
MenuItem {
|
||||
text: "Search a better icon"
|
||||
onClicked: {
|
||||
py.importModule("main", function(){
|
||||
py.call("main.scrape_icon", [iconEditor.attrs.URL], function(result){
|
||||
if(result) {
|
||||
icon.source = result;
|
||||
iconEditor.attrs.Icon = result;
|
||||
notification.sendMessage("A better icon found");
|
||||
} else {
|
||||
notification.sendMessage("Not better icon found");
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: GridItem {
|
||||
onClicked: {
|
||||
activated = !activated
|
||||
iconEditor.corners[idCell] = activated
|
||||
PageHeader {
|
||||
id: header
|
||||
width: parent.width
|
||||
title: "name: " + attrs.Name
|
||||
}
|
||||
|
||||
SilicaGridView {
|
||||
anchors.top: header.bottom
|
||||
width: parent.width
|
||||
height: width
|
||||
cellWidth: width / 2
|
||||
cellHeight: cellWidth
|
||||
|
||||
model: ListModel {
|
||||
id: gridItems
|
||||
|
||||
ListElement {
|
||||
idCell: 0
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 1
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 2
|
||||
activated: true
|
||||
}
|
||||
ListElement {
|
||||
idCell: 3
|
||||
activated: true
|
||||
}
|
||||
}
|
||||
|
||||
delegate: GridItem {
|
||||
onClicked: {
|
||||
activated = !activated
|
||||
iconEditor.corners[idCell] = activated
|
||||
py.importModule("main", function(){
|
||||
py.call("main.sailify", [(iconEditor.attrs.old_icon || iconEditor.attrs.Icon).toString(), iconEditor.corners, size.value], function(result){
|
||||
icon.source = result
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
anchors.top: header.bottom
|
||||
width: parent.width
|
||||
height: width
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: size
|
||||
anchors.top: icon.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
label: "Size:"
|
||||
width: parent.width
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
stepSize: 10
|
||||
value: 100
|
||||
onReleased: {
|
||||
py.importModule("main", function(){
|
||||
py.call("main.sailify", [(iconEditor.attrs.old_icon || iconEditor.attrs.Icon).toString(), iconEditor.corners, size.value], function(result){
|
||||
icon.source = result
|
||||
|
@ -53,66 +116,41 @@ Page {
|
|||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
anchors.top: header.bottom
|
||||
width: parent.width
|
||||
height: width
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: size
|
||||
anchors.top: icon.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
label: "Size:"
|
||||
width: parent.width
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
stepSize: 10
|
||||
value: 100
|
||||
onReleased: {
|
||||
py.importModule("main", function(){
|
||||
py.call("main.sailify", [(iconEditor.attrs.old_icon || iconEditor.attrs.Icon).toString(), iconEditor.corners, size.value], function(result){
|
||||
icon.source = result
|
||||
Button {
|
||||
anchors.top: size.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 20
|
||||
text: "Save"
|
||||
onClicked: {
|
||||
if(iconEditor.attrs.old_icon === undefined) {
|
||||
iconEditor.attrs.old_icon = iconEditor.attrs.Icon.toString()
|
||||
}
|
||||
iconEditor.attrs.Icon = icon.source.toString()
|
||||
py.importModule("main", function(){
|
||||
py.call("main.save_icon", [iconEditor.attrs], function(result){
|
||||
if(result){
|
||||
pageStack.pop()
|
||||
pageStack.completeAnimation()
|
||||
pageStack.currentPage.mainList.reload()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.top: size.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 20
|
||||
text: "Save"
|
||||
onClicked: {
|
||||
if(iconEditor.attrs.old_icon === undefined) {
|
||||
iconEditor.attrs.old_icon = iconEditor.attrs.Icon.toString()
|
||||
}
|
||||
iconEditor.attrs.Icon = icon.source.toString()
|
||||
py.importModule("main", function(){
|
||||
py.call("main.save_icon", [iconEditor.attrs], function(result){
|
||||
if(result){
|
||||
pageStack.pop()
|
||||
pageStack.completeAnimation()
|
||||
pageStack.currentPage.mainList.reload()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Python {
|
||||
id: py
|
||||
Component.onCompleted: {
|
||||
py.addImportPath(Qt.resolvedUrl("../src"));
|
||||
py.importModule("main", function(){
|
||||
py.call("main.sailify", [(iconEditor.attrs.old_icon || iconEditor.attrs.Icon).toString(), [1, 1, 1, 1], size.value], function(result){
|
||||
icon.source = result
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Python {
|
||||
id: py
|
||||
Component.onCompleted: {
|
||||
py.addImportPath(Qt.resolvedUrl("../src"));
|
||||
py.importModule("main", function(){
|
||||
py.call("main.sailify", [(iconEditor.attrs.old_icon || iconEditor.attrs.Icon).toString(), [1, 1, 1, 1], size.value], function(result){
|
||||
icon.source = result
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import logging
|
||||
|
||||
def log_error(f):
|
||||
def wrap(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logging.error(f"Error on: {f.__name__}")
|
||||
logging.error(f"args: {args}")
|
||||
logging.error(f"kwargs: {kwargs}")
|
||||
logging.error(f"Exception: {e}")
|
||||
raise e
|
||||
return wrap
|
42
src/main.py
42
src/main.py
|
@ -1,4 +1,5 @@
|
|||
import pyotherside # pyright: ignore reportMissingImports
|
||||
from decorators import log_error
|
||||
from PIL import Image, ImageDraw, ImageChops
|
||||
from PIL.Image import Image as ImageType
|
||||
from io import BytesIO
|
||||
|
@ -6,7 +7,9 @@ from glob import glob
|
|||
from collections import Counter
|
||||
from subprocess import getstatusoutput
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urljoin
|
||||
from functools import lru_cache
|
||||
from lxml import etree
|
||||
import requests
|
||||
import base64
|
||||
import os
|
||||
|
@ -16,6 +19,10 @@ import tempfile
|
|||
|
||||
ICON_SIZE = (256, 256)
|
||||
|
||||
HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0"
|
||||
}
|
||||
|
||||
logging_filename = os.path.join(os.path.expanduser("~"),
|
||||
".local",
|
||||
"share",
|
||||
|
@ -69,7 +76,7 @@ def avg_colors(img):
|
|||
|
||||
def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType:
|
||||
img = img.copy()
|
||||
size = size / 100
|
||||
size = max(size, 1) / 100
|
||||
bg = Image.new("RGBA", img.size, bg_tuple)
|
||||
img.thumbnail((value * size for value in img.size))
|
||||
bg.paste(img, ((bg.size[0] - img.size[0]) // 2, (bg.size[1] - img.size[1]) // 2), img)
|
||||
|
@ -123,6 +130,39 @@ def get_web_icons():
|
|||
|
||||
return list(map(parse_file, apps))
|
||||
|
||||
@log_error
|
||||
def scrape_icon(url: str) -> str:
|
||||
url_parsed = urlparse(url)
|
||||
origin = f"{url_parsed.scheme}://{url_parsed.netloc}"
|
||||
html = etree.HTML(requests.get(origin, headers=HEADERS).text)
|
||||
|
||||
possible_svg = None
|
||||
possible_icon = None
|
||||
max_size = 0
|
||||
|
||||
for icon in html.xpath("//link[contains(@rel, 'icon')]"):
|
||||
if "SVG" in next(iter(icon.xpath("@type")), "").upper():
|
||||
possible_svg = icon.xpath("@href")[0]
|
||||
break
|
||||
possible_href = next(iter(icon.xpath("@href")), None)
|
||||
possible_size = next(iter(icon.xpath("@sizes")), None)
|
||||
possible_rel = next(iter(icon.xpath("@rel")), "")
|
||||
if possible_size and possible_href and possible_size != "any":
|
||||
size = int(possible_size.split("x")[0])
|
||||
if size > max_size:
|
||||
max_size = size
|
||||
possible_icon = possible_href
|
||||
if possible_href and not possible_size and possible_rel.upper() == "apple-touch-icon".upper():
|
||||
possible_icon = possible_href
|
||||
|
||||
new_icon = possible_svg or possible_icon
|
||||
if new_icon:
|
||||
parsed_new_icon = urlparse(new_icon)
|
||||
if not parsed_new_icon.scheme or not parsed_new_icon.netloc:
|
||||
new_icon = urljoin(origin, new_icon)
|
||||
return sailify(new_icon, [True] * 4, 100)
|
||||
|
||||
@log_error
|
||||
def sailify(raw_str, pattern, size) -> str:
|
||||
url_parsed = urlparse(raw_str)
|
||||
if url_parsed.scheme == "data":
|
||||
|
|
Loading…
Reference in New Issue