nostalgia/deps/QDark/qdarkstyle/utils/scss.py

278 lines
8.0 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Utilities for compiling SASS files."""
# Standard library imports
import keyword
import logging
import os
import re
import shutil
import sys
# Third party imports
import qtsass
# Local imports
from qdarkstyle import (MAIN_SCSS_FILE, MAIN_SCSS_FILEPATH, QSS_PATH,
QSS_FILEPATH, RC_PATH, QSS_FILE,
VARIABLES_SCSS_FILE, VARIABLES_SCSS_FILEPATH)
from qdarkstyle.palette import DarkPalette
from qdarkstyle.utils.images import create_images, create_palette_image
# Constants
PY2 = sys.version[0] == '2'
HEADER_SCSS = '''// ---------------------------------------------------------------------------
//
// File created programmatically
//
// The definitions are in the "qdarkstyle.palette" module
//
// WARNING! All changes made in this file will be lost!
//
//----------------------------------------------------------------------------
'''
HEADER_QSS = '''/* ---------------------------------------------------------------------------
Created by the qtsass compiler v{}
The definitions are in the "qdarkstyle.qss._styles.scss" module
WARNING! All changes made in this file will be lost!
--------------------------------------------------------------------------- */
'''
_logger = logging.getLogger(__name__)
def _dict_to_scss(data):
"""Create a scss variables string from a dict."""
lines = []
template = "${}: {};"
for key, value in data.items():
line = template.format(key, value)
lines.append(line)
return '\n'.join(lines)
def _scss_to_dict(string):
"""Parse variables and return a dict."""
data = {}
lines = string.split('\n')
for line in lines:
line = line.strip()
if line and line.startswith('$'):
key, value = line.split(':')
key = key[1:].strip()
key = key.replace('-', '_')
value = value.split(';')[0].strip()
data[key] = value
return data
def _create_scss_variables(variables_scss_filepath, palette,
header=HEADER_SCSS):
"""Create a scss variables file."""
scss = _dict_to_scss(palette.to_dict())
data = header + scss + '\n'
with open(variables_scss_filepath, 'w') as f:
f.write(data)
def _create_qss(main_scss_path, qss_filepath, header=HEADER_QSS):
"""Create a styles.qss file from qtsass."""
data = ''
qtsass.compile_filename(main_scss_path, qss_filepath,
output_style='expanded')
with open(qss_filepath, 'r') as f:
data = f.read()
data = header.format(qtsass.__version__) + data
with open(qss_filepath, 'w') as f:
f.write(data)
return data
def create_qss(qss_filepath=QSS_FILEPATH, main_scss_filepath=MAIN_SCSS_FILEPATH,
variables_scss_filepath=VARIABLES_SCSS_FILEPATH,
palette=DarkPalette):
"""Create variables files and run qtsass compilation."""
_create_scss_variables(variables_scss_filepath, palette)
stylesheet = _create_qss(main_scss_filepath, qss_filepath)
return stylesheet
def is_identifier(name):
"""Check that `name` string is a valid identifier in Python."""
if PY2:
is_not_keyword = name not in keyword.kwlist
pattern = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
matches_pattern = bool(pattern.match(name))
check = is_not_keyword and matches_pattern
else:
check = name.isidentifier()
return check
def create_custom_qss(
name,
path,
color_background_light,
color_background_normal,
color_background_dark,
color_foreground_light,
color_foreground_normal,
color_foreground_dark,
color_selection_light,
color_selection_normal,
color_selection_dark,
border_radius,
):
"""
Create a custom palette based on the parameters defined.
The `name` must be a valid Python identifier and will be stored
as a lowercased folder (even if the identifier had uppercase letters).
This fuction returns the custom stylesheet pointing to resources stored at
.../path/name/.
"""
stylesheet = ''
# Check if name is valid
if is_identifier(name):
name = name if name[0].isupper() else name.capitalize()
else:
raise Exception('The custom palette name must be a valid Python '
'identifier!')
# Copy resources folder
rc_loc = os.path.basename(RC_PATH)
qss_loc = os.path.basename(QSS_PATH)
theme_root_path = os.path.join(path, name.lower())
theme_rc_path = os.path.join(theme_root_path, rc_loc)
if os.path.isdir(theme_root_path):
shutil.rmtree(theme_root_path)
shutil.copytree(RC_PATH, theme_rc_path)
# Copy QSS folder and contents
theme_qss_path = os.path.join(theme_root_path, qss_loc)
if os.path.isdir(theme_qss_path):
os.removedirs(theme_qss_path)
shutil.copytree(QSS_PATH, theme_qss_path)
# Create custom palette
custom_palette = type(name, (DarkPalette, ), {})
custom_palette.COLOR_BACKGROUND_LIGHT = color_background_light
custom_palette.COLOR_BACKGROUND_NORMAL = color_background_normal
custom_palette.COLOR_BACKGROUND_DARK = color_background_dark
custom_palette.COLOR_FOREGROUND_LIGHT = color_foreground_light
custom_palette.COLOR_FOREGROUND_NORMAL = color_foreground_normal
custom_palette.COLOR_FOREGROUND_DARK = color_foreground_dark
custom_palette.COLOR_SELECTION_LIGHT = color_selection_light
custom_palette.COLOR_SELECTION_NORMAL = color_selection_normal
custom_palette.COLOR_SELECTION_DARK = color_selection_dark
custom_palette.SIZE_BORDER_RADIUS = border_radius
custom_palette.PATH_RESOURCES = "'{}'".format(theme_root_path)
# Process images and save them to the custom platte rc folder
create_images(rc_path=theme_rc_path, palette=custom_palette)
create_palette_image(path=theme_root_path, palette=custom_palette)
# Compile SCSS
variables_scss_filepath = os.path.join(theme_qss_path, VARIABLES_SCSS_FILE)
theme_main_scss_filepath = os.path.join(theme_qss_path, MAIN_SCSS_FILE)
theme_qss_filepath = os.path.join(theme_root_path, QSS_FILE)
stylesheet = create_qss(
qss_filepath=theme_qss_filepath,
main_scss_filepath=theme_main_scss_filepath,
variables_scss_filepath=variables_scss_filepath,
palette=custom_palette,
)
# Update colors in text
with open(theme_main_scss_filepath, 'r') as fh:
data = fh.read()
for key, color in DarkPalette.color_palette().items():
custom_color = custom_palette.color_palette()[key].upper()
data = data.replace(color, custom_color)
stylesheet = stylesheet.replace(color, custom_color)
with open(theme_main_scss_filepath, 'w') as fh:
fh.write(data)
with open(theme_qss_filepath, 'w') as fh:
fh.write(stylesheet)
return stylesheet
def create_custom_qss_from_palette(name, path, palette):
"""
Create a custom palette based on a palette class.
"""
kwargs = {
'name': name,
'path': path,
'border_radius': palette.SIZE_BORDER_RADIUS,
}
kwargs.update(palette.color_palette())
stylesheet = create_custom_qss(**kwargs)
return stylesheet
def create_custom_qss_from_dict(name, path, palette_dict):
"""
Create a custom palette based on a palette dictionary.
"""
kwargs = {
'name': name,
'path': path,
'border_radius': palette_dict.get('SIZE_BORDER_RADIUS', '4px'),
}
kwargs.update(palette_dict)
stylesheet = create_custom_qss(**kwargs)
return stylesheet
if __name__ == '__main__':
# Example of a custom palette
# TODO: change to not use a specfic path
# TODO: may move to other place, e.g., example.py
qss = create_custom_qss(
'MyAwesomePalette',
'/Users/gpena-castellanos/Desktop',
'#ff0000',
'#cc0000',
'#aa0000',
'#00ff00',
'#00cc00',
'#00aa00',
'#0000ff',
'#0000cc',
'#0000aa',
'0px',
)