backend: create new modules for preset generation utilities

This commit is contained in:
tfuxu 2022-12-08 14:05:38 +01:00
parent e834e55b11
commit 22f476f766
No known key found for this signature in database
GPG key ID: 79CFC3B9B31C098A
7 changed files with 271 additions and 129 deletions

View file

View file

@ -0,0 +1,8 @@
themingdir = 'gradience/backend/theming'
gradience_sources = [
'__init__.py',
'monet.py',
'preset_utils.py'
]
PY_INSTALLDIR.install_sources(gradience_sources, subdir: themingdir)

View file

@ -0,0 +1,68 @@
# monet.py
#
# Change the look of Adwaita, with ease
# Copyright (C) 2022 Gradience Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
import material_color_utilities_python as monet
from gradience.backend.models.preset import Preset
from gradience.backend.logger import Logger
logging = Logger()
class Monet:
def __init__(self):
self.palette = None
def generate_from_image(self, image_path) -> dict:
#TODO: Test SVG support? I don't know what's that gradience_bg.png /
# and why it is used for SVG images
if image_path.endswith(".svg"):
drawing = svg2rlg(image_path)
image_path = os.path.join(
os.environ.get("XDG_RUNTIME_DIR"), "gradience_bg.png"
)
renderPM.drawToFile(drawing, image_path, fmt="PNG")
if image_path.endswith(".xml"):
logging.error(f"XML files are unsupported by Gradience's Monet implementation.")
return False
try:
monet_img = monet.Image.open(image_path)
except Exception as e:
logging.error(f"An error occurred while generating a Monet palette. Exc: {e}")
return False
else:
basewidth = 64
wpercent = basewidth / float(monet_img.size[0])
hsize = int((float(monet_img.size[1]) * float(wpercent)))
monet_img = monet_img.resize(
(basewidth, hsize), monet.Image.Resampling.LANCZOS
)
self.palette = monet.themeFromImage(monet_img)
return self.palette
def palette_to_dict(self, palette):
pass

View file

@ -0,0 +1,174 @@
# preset_utils.py
#
# Change the look of Adwaita, with ease
# Copyright (C) 2022 Gradience Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import json
import material_color_utilities_python as monet
from gradience.backend.theming.monet import Monet
from gradience.backend.models.preset import Preset
from gradience.backend.utils.colors import rgba_from_argb
from gradience.backend.logger import Logger
logging = Logger()
class PresetUtils:
def __init__(self):
self.preset = Preset()
def new_preset_from_monet(self, name=None, monet_palette=None, props=None, vars_only=False) -> dict or bool:
if props:
tone = props[0]
theme = props[1]
else:
raise Exception("Properties 'tone' and/or 'theme' missing")
if not monet_palette:
raise Exception("Property 'monet_palette' missing")
if theme == "dark":
dark_theme = monet_palette["schemes"]["dark"]
variable = {
"accent_color": rgba_from_argb(dark_theme.primary),
"accent_bg_color": rgba_from_argb(dark_theme.primaryContainer),
"accent_fg_color": rgba_from_argb(dark_theme.onPrimaryContainer),
"destructive_color": rgba_from_argb(dark_theme.error),
"destructive_bg_color": rgba_from_argb(dark_theme.errorContainer),
"destructive_fg_color": rgba_from_argb(
dark_theme.onErrorContainer
),
"success_color": rgba_from_argb(dark_theme.tertiary),
"success_bg_color": rgba_from_argb(dark_theme.onTertiary),
"success_fg_color": rgba_from_argb(dark_theme.onTertiaryContainer),
"warning_color": rgba_from_argb(dark_theme.secondary),
"warning_bg_color": rgba_from_argb(dark_theme.onSecondary),
"warning_fg_color": rgba_from_argb(dark_theme.primary, "0.8"),
"error_color": rgba_from_argb(dark_theme.error),
"error_bg_color": rgba_from_argb(dark_theme.errorContainer),
"error_fg_color": rgba_from_argb(dark_theme.onError),
"window_bg_color": rgba_from_argb(dark_theme.surface),
"window_fg_color": rgba_from_argb(dark_theme.onSurface),
"view_bg_color": rgba_from_argb(dark_theme.surface),
"view_fg_color": rgba_from_argb(dark_theme.onSurface),
"headerbar_bg_color": rgba_from_argb(dark_theme.surface),
"headerbar_fg_color": rgba_from_argb(dark_theme.onSurface),
"headerbar_border_color": rgba_from_argb(
dark_theme.primary, "0.8"
),
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": rgba_from_argb(dark_theme.shadow),
"card_bg_color": rgba_from_argb(dark_theme.primary, "0.05"),
"card_fg_color": rgba_from_argb(dark_theme.onSecondaryContainer),
"card_shade_color": rgba_from_argb(dark_theme.shadow),
"dialog_bg_color": rgba_from_argb(dark_theme.secondaryContainer),
"dialog_fg_color": rgba_from_argb(dark_theme.onSecondaryContainer),
"popover_bg_color": rgba_from_argb(dark_theme.secondaryContainer),
"popover_fg_color": rgba_from_argb(
dark_theme.onSecondaryContainer
),
"shade_color": rgba_from_argb(dark_theme.shadow),
"scrollbar_outline_color": rgba_from_argb(dark_theme.outline),
}
elif theme == "light":
light_theme = monet_palette["schemes"]["light"]
variable = {
"accent_color": rgba_from_argb(light_theme.primary),
"accent_bg_color": rgba_from_argb(light_theme.primary),
"accent_fg_color": rgba_from_argb(light_theme.onPrimary),
"destructive_color": rgba_from_argb(light_theme.error),
"destructive_bg_color": rgba_from_argb(light_theme.errorContainer),
"destructive_fg_color": rgba_from_argb(
light_theme.onErrorContainer
),
"success_color": rgba_from_argb(light_theme.tertiary),
"success_bg_color": rgba_from_argb(light_theme.tertiaryContainer),
"success_fg_color": rgba_from_argb(
light_theme.onTertiaryContainer
),
"warning_color": rgba_from_argb(light_theme.secondary),
"warning_bg_color": rgba_from_argb(light_theme.secondaryContainer),
"warning_fg_color": rgba_from_argb(
light_theme.onSecondaryContainer
),
"error_color": rgba_from_argb(light_theme.error),
"error_bg_color": rgba_from_argb(light_theme.errorContainer),
"error_fg_color": rgba_from_argb(light_theme.onError),
"window_bg_color": rgba_from_argb(light_theme.secondaryContainer),
"window_fg_color": rgba_from_argb(light_theme.onSurface),
"view_bg_color": rgba_from_argb(light_theme.secondaryContainer),
"view_fg_color": rgba_from_argb(light_theme.onSurface),
"headerbar_bg_color": rgba_from_argb(
light_theme.secondaryContainer
),
"headerbar_fg_color": rgba_from_argb(light_theme.onSurface),
"headerbar_border_color": rgba_from_argb(
light_theme.primary, "0.8"
),
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": rgba_from_argb(
light_theme.secondaryContainer
),
"card_bg_color": rgba_from_argb(light_theme.primary, "0.05"),
"card_fg_color": rgba_from_argb(light_theme.onSecondaryContainer),
"card_shade_color": rgba_from_argb(light_theme.shadow),
"dialog_bg_color": rgba_from_argb(light_theme.secondaryContainer),
"dialog_fg_color": rgba_from_argb(
light_theme.onSecondaryContainer
),
"popover_bg_color": rgba_from_argb(light_theme.secondaryContainer),
"popover_fg_color": rgba_from_argb(
light_theme.onSecondaryContainer
),
"shade_color": rgba_from_argb(light_theme.shadow),
"scrollbar_outline_color": rgba_from_argb(light_theme.outline),
}
if vars_only == False and not name:
raise Exception("You either need to set 'vars_only' property to True, or add value to 'name' property")
if vars_only:
return variable
self.preset.new(display_name=name, variables=variable)
'''preset_dict = {
"name": self.preset.display_name,
"variables": self.preset.variables,
"palette": self.preset.palette,
"custom_css": self.preset.custom_css,
"plugins": self.preset.plugins,
}
logging.debug("Generated Monet preset:\n" + json.dumps(preset_dict, indent=4))'''
try:
self.preset.save_to_file(name=name)
except Exception as e:
# TODO: Move exception handling to model/preset module
logging.error(f"Unexpected file error while trying to generate preset from generated Monet palette. Exc: {e}")
return False
return True
if __name__ == "__main__":
preset_utils = PresetUtils()
monet_palette = Monet().generate_from_image("/home/tfuxc/Pictures/Wallpapers/wallhaven-57kzw1.png")
props = [20, "dark"]
preset_utils.new_preset_from_monet("My awesome Monet", monet_palette, props)

View file

@ -23,7 +23,7 @@ import subprocess
from anyascii import anyascii
def to_slug_case(non_slug):
def to_slug_case(non_slug) -> str:
return re.sub(r"[^0-9a-z]+", "-", anyascii(non_slug).lower()).strip("-")
def run_command(command, *args, **kwargs):

View file

@ -28,6 +28,7 @@ from gi.repository import Gtk, Gdk, Gio, Adw, GLib, Xdp, XdpGtk4
from gradience.backend.globals import presets_dir
from gradience.backend.css_parser import parse_css
from gradience.backend.models.preset import Preset
from gradience.backend.theming.preset_utils import PresetUtils
from gradience.backend.utils.colors import rgba_from_argb
from gradience.backend.utils.common import to_slug_case
from gradience.backend.constants import *
@ -393,102 +394,8 @@ class GradienceApplication(Adw.Application):
else:
monet_theme = "light"
if monet_theme == "dark":
dark_theme = theme["schemes"]["dark"]
variable = {
"accent_color": self.rgba_from_argb(dark_theme.primary),
"accent_bg_color": self.rgba_from_argb(dark_theme.primaryContainer),
"accent_fg_color": self.rgba_from_argb(dark_theme.onPrimaryContainer),
"destructive_color": self.rgba_from_argb(dark_theme.error),
"destructive_bg_color": self.rgba_from_argb(dark_theme.errorContainer),
"destructive_fg_color": self.rgba_from_argb(
dark_theme.onErrorContainer
),
"success_color": self.rgba_from_argb(dark_theme.tertiary),
"success_bg_color": self.rgba_from_argb(dark_theme.onTertiary),
"success_fg_color": self.rgba_from_argb(dark_theme.onTertiaryContainer),
"warning_color": self.rgba_from_argb(dark_theme.secondary),
"warning_bg_color": self.rgba_from_argb(dark_theme.onSecondary),
"warning_fg_color": self.rgba_from_argb(dark_theme.primary, "0.8"),
"error_color": self.rgba_from_argb(dark_theme.error),
"error_bg_color": self.rgba_from_argb(dark_theme.errorContainer),
"error_fg_color": self.rgba_from_argb(dark_theme.onError),
"window_bg_color": self.rgba_from_argb(dark_theme.surface),
"window_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"view_bg_color": self.rgba_from_argb(dark_theme.surface),
"view_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"headerbar_bg_color": self.rgba_from_argb(dark_theme.surface),
"headerbar_fg_color": self.rgba_from_argb(dark_theme.onSurface),
"headerbar_border_color": self.rgba_from_argb(
dark_theme.primary, "0.8"
),
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": self.rgba_from_argb(dark_theme.shadow),
"card_bg_color": self.rgba_from_argb(dark_theme.primary, "0.05"),
"card_fg_color": self.rgba_from_argb(dark_theme.onSecondaryContainer),
"card_shade_color": self.rgba_from_argb(dark_theme.shadow),
"dialog_bg_color": self.rgba_from_argb(dark_theme.secondaryContainer),
"dialog_fg_color": self.rgba_from_argb(dark_theme.onSecondaryContainer),
"popover_bg_color": self.rgba_from_argb(dark_theme.secondaryContainer),
"popover_fg_color": self.rgba_from_argb(
dark_theme.onSecondaryContainer
),
"shade_color": self.rgba_from_argb(dark_theme.shadow),
"scrollbar_outline_color": self.rgba_from_argb(dark_theme.outline),
}
else: # light
light_theme = theme["schemes"]["light"]
variable = {
"accent_color": self.rgba_from_argb(light_theme.primary),
"accent_bg_color": self.rgba_from_argb(light_theme.primary),
"accent_fg_color": self.rgba_from_argb(light_theme.onPrimary),
"destructive_color": self.rgba_from_argb(light_theme.error),
"destructive_bg_color": self.rgba_from_argb(light_theme.errorContainer),
"destructive_fg_color": self.rgba_from_argb(
light_theme.onErrorContainer
),
"success_color": self.rgba_from_argb(light_theme.tertiary),
"success_bg_color": self.rgba_from_argb(light_theme.tertiaryContainer),
"success_fg_color": self.rgba_from_argb(
light_theme.onTertiaryContainer
),
"warning_color": self.rgba_from_argb(light_theme.secondary),
"warning_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"warning_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"error_color": self.rgba_from_argb(light_theme.error),
"error_bg_color": self.rgba_from_argb(light_theme.errorContainer),
"error_fg_color": self.rgba_from_argb(light_theme.onError),
"window_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"window_fg_color": self.rgba_from_argb(light_theme.onSurface),
"view_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"view_fg_color": self.rgba_from_argb(light_theme.onSurface),
"headerbar_bg_color": self.rgba_from_argb(
light_theme.secondaryContainer
),
"headerbar_fg_color": self.rgba_from_argb(light_theme.onSurface),
"headerbar_border_color": self.rgba_from_argb(
light_theme.primary, "0.8"
),
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": self.rgba_from_argb(
light_theme.secondaryContainer
),
"card_bg_color": self.rgba_from_argb(light_theme.primary, "0.05"),
"card_fg_color": self.rgba_from_argb(light_theme.onSecondaryContainer),
"card_shade_color": self.rgba_from_argb(light_theme.shadow),
"dialog_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"dialog_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"popover_bg_color": self.rgba_from_argb(light_theme.secondaryContainer),
"popover_fg_color": self.rgba_from_argb(
light_theme.onSecondaryContainer
),
"shade_color": self.rgba_from_argb(light_theme.shadow),
"scrollbar_outline_color": self.rgba_from_argb(light_theme.outline),
}
variable = PresetUtils().new_preset_from_monet(monet_palette=monet,
props=[tone, monet_theme], vars_only=True)
for key in variable:
if key in self.pref_variables:
@ -864,7 +771,7 @@ class GradienceApplication(Adw.Application):
def on_theme_set_dialog_response (self, dialog, response):
if response == "ok":
print("theme_set_dialog_ok")
logging.debug("theme_set_dialog_ok")
def restore_color_scheme(self, widget, response):
if response == "restore":
@ -927,7 +834,7 @@ class GradienceApplication(Adw.Application):
def on_theme_restore_dialog_response (self, dialog, response):
if response == "ok":
print("theme_restore_dialog_ok")
logging.debug("theme_restore_dialog_ok")
def reset_color_scheme(self, widget, response):
if response == "reset":
@ -981,7 +888,7 @@ class GradienceApplication(Adw.Application):
def on_theme_reset_dialog_response (self, dialog, response):
if response == "ok":
print("theme_reset_dialog_ok")
logging.debug("theme_reset_dialog_ok")
def show_preferences(self, *_args):
prefs = GradiencePreferencesWindow(self.win)

View file

@ -23,6 +23,7 @@ from reportlab.graphics import renderPM
from material_color_utilities_python import *
from gi.repository import Gtk, Adw, Gio
from gradience.backend.theming.monet import Monet
from gradience.backend.constants import rootdir, app_id, build_type
from gradience.frontend.widgets.error_list_row import GradienceErrorListRow
@ -70,11 +71,11 @@ class GradienceMainWindow(Adw.ApplicationWindow):
self.connect("unrealize", self.save_window_props)
self.style_manager = self.get_application().style_manager
self.first_apply = True
#self.first_apply = True
self.get_default_wallpaper()
# FIXME: This function works only when building using meson, because Flatpak
# FIXME: This function works only when building using meson, because Flatpak \
# can't access host's dconf with current config/impl
def get_default_wallpaper(self):
background_settings = Gio.Settings("org.gnome.desktop.background")
@ -210,39 +211,23 @@ class GradienceMainWindow(Adw.ApplicationWindow):
def on_apply_button(self, *_args):
if self.monet_image_file:
if self.monet_image_file.endswith(".svg"):
drawing = svg2rlg(self.monet_image_file)
self.monet_image_file = os.path.join(
os.environ.get("XDG_RUNTIME_DIR"), "gradience_bg.png"
)
renderPM.drawToFile(drawing, self.monet_image_file, fmt="PNG")
if self.monet_image_file.endswith(".xml"):
logging.debug("XML WIP")
try:
self.monet_img = Image.open(self.monet_image_file)
except Exception:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Unsupported background type"))
)
else:
basewidth = 64
wpercent = basewidth / float(self.monet_img.size[0])
hsize = int((float(self.monet_img.size[1]) * float(wpercent)))
self.monet_img = self.monet_img.resize(
(basewidth, hsize), Image.Resampling.LANCZOS
)
self.theme = themeFromImage(self.monet_img)
self.theme = Monet().generate_from_image(self.monet_image_file)
self.tone = self.tone_row.get_selected_item()
self.monet_theme = self.monet_theme_row.get_selected_item()
self.get_application().update_theme_from_monet(
self.theme, self.tone, self.monet_theme
)
if not self.first_apply:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Palette generated"))
)
except Exception as e:
logging.error(f"Failed to generate Monet palette. Exc: {e}")
self.toast_overlay.add_toast(
Adw.Toast(title=_("Failed to generate Monet palette"))
)
else:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Palette generated"))
)
else:
self.toast_overlay.add_toast(
Adw.Toast(title=_("Select a background first"))