pluslib(fix):

- Added logic to apply the wallpaper to all the monitors and workspaces
- Fixed a breaking bug where icons were not generated as the folders were iterated and not their contents, causing mogrify to throw an error and break the script
- Same for the cursors
This commit is contained in:
XargonWan 2024-11-21 10:54:58 +09:00
parent bdf5cf36a1
commit 66f41bf6c4

View File

@ -885,8 +885,8 @@ class ChicagoPlus:
os.mkdir(self.folder_names[i])
def create_icons(self, create_48_document_icon = True):
self.logger.info("Creating new icons in {}".format(self.folder_names['icons']))
self.logger.info(f"Creating new icons in {self.folder_names['icons']}")
svg_file_names = {}
png_file_names = {
"my_computer" : "user-home.png",
@ -897,101 +897,108 @@ class ChicagoPlus:
}
for i in png_file_names:
svg_file_names[i] = png_file_names[i].replace(".png",".svg")
svg_file_names[i] = png_file_names[i].replace(".png", ".svg")
for iconname in self.theme_config['icons']:
self.logger.debug(f"Processing icon: {iconname}")
self.logger.info(f"Processing icon: {iconname}")
if not self.theme_config['icons'][iconname]:
self.logger.debug("{:<21} | Icon does not exist in this theme".format(iconname))
self.logger.warning(f"{iconname:<21} | Icon does not exist in this theme")
continue
icon_sizes = [16,22,24,32,48]
icon_sizes = [16, 22, 24, 32, 48]
filename = self.theme_config['icons'][iconname]['filename']
index = self.theme_config['icons'][iconname]['index']
path = self.theme_config['icons'][iconname]['path']
filetype = self.theme_config['icons'][iconname]['type']
self.logger.debug(f"Icon details - Name: {filename}, Index: {index}, Path: {path}, Type: {filetype}")
if not path:
self.logger.error("{:<21} | {} does not exist in this theme".format(iconname, filename))
self.logger.warning(f"{iconname:<21} | {filename} does not exist in this theme")
continue
if filetype not in ['dll', 'icl', 'ico', 'bmp']:
# wut
self.logger.error("File type {} not supported: {}".format(filetype, filename))
self.logger.warning(f"File type {filetype} not supported: {filename}")
continue
self.logger.info("{:<21} | {}".format(iconname, filename))
self.logger.info(f"{iconname:<21} | {filename}")
if filetype in ['dll', 'icl']:
# Get the icons stored in the DLL
self.logger.debug("{:<21} | Icons are stored in ICL file {}".format("", filename, path))
icon_files = self.extract_icons_from_dll(path)
else:
self.logger.debug("{:<21} | Icons are stored in ICO file {} {}".format("", filename,path))
self.logger.debug(f"{iconname:<21} | Icons are stored in ICO file {filename} {path}")
icon_files = self.extract_ico(path)
if icon_files == 'bmp':
filetype = 'bmp'
if not icon_files:
self.logger.error("Not a valid icon file: {}".format(self.theme_config['icons'][iconname]))
self.logger.error(f"Not a valid icon file: {self.theme_config['icons'][iconname]}")
continue
# If the icons exist at various sizes, write them and convert them instead of scaling the largest one
for size in icon_sizes:
self.logger.debug("{:<21} | Searching for icon size: {} in {}".format("", size, filename))
self.logger.debug(f"{iconname:<21} | Searching for icon size: {size} in {filename}")
if filetype in ['dll','icl']:
if filetype in ['dll', 'icl']:
icon_filename, icon_file = self.get_icons_size_dll(icon_files, index, size)
elif filetype in 'ico':
elif filetype == 'ico':
icon_filename, icon_file = self.get_icons_size_ico(icon_files, size)
else:
icon_filename = False
if icon_filename:
icon_sizes.remove(size)
f = open(self.folder_names['icons']+icon_filename,"wb")
f.write(icon_file)
f.close()
sized_target = self.folder_names['icons']+"places/"+str(size)+"/"+png_file_names[iconname]
self.logger.debug("{:<21} | Creating: {} {} {}".format("", size, self.folder_names['icons']+icon_filename, sized_target))
self.convert_ico_files(self.folder_names['icons']+icon_filename, sized_target)
destination = os.path.join(self.folder_names['icons'], icon_filename)
with open(destination, "wb") as f:
f.write(icon_file)
sized_target = os.path.join(
self.folder_names['icons'], "places", str(size), png_file_names[iconname]
)
self.logger.debug(f"{iconname:<21} | Creating: {size} {destination} {sized_target}")
self.convert_ico_files(destination, sized_target)
# Now that we're done, get the largest file and use that for the rest
if filetype in ['dll', 'icl']:
icon_filename, icon_file = self.get_largest_icon_dll(icon_files, index)
elif filetype in 'ico':
elif filetype == 'ico':
icon_filename, icon_file = self.get_largest_icon_ico(icon_files, size)
if filetype in ['dll', 'icl', 'ico'] and not isinstance(icon_file, str):
f = open(self.folder_names['icons']+icon_filename,"wb")
f.write(icon_file)
f.close()
destination = os.path.join(self.folder_names['icons'], icon_filename)
with open(destination, "wb") as f:
f.write(icon_file)
else:
shutil.copyfile(path, self.folder_names['icons']+icon_filename)
svg_icon_file = self.convert_icon(self.folder_names['icons'], self.folder_names['icons']+icon_filename)
for size in icon_sizes:
if size <= 32 and iconname == "documents_ico" and not create_48_document_icon:
continue
sized_target = self.folder_names['icons']+"places/"+str(size)+"/"+png_file_names[iconname]
self.logger.debug("{:<21} | Creating: {} {} {}".format("", size, svg_icon_file, sized_target))
self.convert_to_png_with_inkscape( svg_icon_file, size, sized_target)
destination = os.path.join(self.folder_names['icons'], os.path.basename(icon_filename))
shutil.copyfile(path, destination)
scaled_target = self.folder_names['icons']+"places/scalable/"+svg_file_names[iconname]
svg_icon_file = self.convert_icon(self.folder_names['icons'], destination)
for size in icon_sizes:
if size <= 32 and iconname == "documents_ico" and not create_48_document_icon:
continue
sized_target = os.path.join(
self.folder_names['icons'], "places", str(size), png_file_names[iconname]
)
self.logger.debug(f"{iconname:<21} | Creating: {size} {svg_icon_file} {sized_target}")
self.convert_to_png_with_inkscape(svg_icon_file, size, sized_target)
scaled_target = os.path.join(
self.folder_names['icons'], "places", "scalable", svg_file_names[iconname]
)
shutil.copy(svg_icon_file, scaled_target)
# Update index.theme
self.logger.debug("Updating icon index.theme file")
icon_theme_config = configparser.RawConfigParser(interpolation=None)
icon_theme_config.optionxform = str
icon_theme_config.read(self.folder_names['icons']+"/index.theme")
icon_theme_config.set("Icon Theme","Name",self.index_theme_name)
with open(self.folder_names['icons']+"/index.theme", 'w') as configfile:
icon_theme_config.write(configfile, space_around_delimiters=False)
icon_theme_config.read(os.path.join(self.folder_names['icons'], "index.theme"))
icon_theme_config.set("Icon Theme", "Name", self.index_theme_name)
with open(os.path.join(self.folder_names['icons'], "index.theme"), 'w') as configfile:
icon_theme_config.write(configfile, space_around_delimiters=False)
def create_cursors(self):
self.logger.info("Creating new xcursors in {}".format(self.folder_names['cursors']))
@ -1799,25 +1806,64 @@ class ChicagoPlus:
}
for i in ['gtk-3.0/assets/','gtk-3.24/assets/','gtk-3.0/scrollbar/','gtk-3.24/scrollbar/']:
folder = path + i
for i in ['gtk-3.0/assets/', 'gtk-3.24/assets/', 'gtk-3.0/scrollbar/', 'gtk-3.24/scrollbar/']:
folder = path + i
for asset in os.listdir(folder):
if not asset.startswith("status") and not asset.startswith("branding"):
self.logger.debug("mogrifying {} ({} {} {} {} {} {})".format(asset, ButtonDKShadow, ButtonLight, ButtonShadow, ButtonHilight, ButtonFace, ButtonText))
asset_path = os.path.join(folder, asset) # Get full path of the asset
# If the asset is a directory, iterate over its contents
if os.path.isdir(asset_path):
self.logger.info(f"Entering directory: {asset_path}")
for sub_asset in os.listdir(asset_path):
sub_asset_path = os.path.join(asset_path, sub_asset)
if os.path.isfile(sub_asset_path): # Ensure it's a valid file
self.logger.debug(
f"mogrifying {sub_asset} in {asset_path} "
f"({ButtonDKShadow}, {ButtonLight}, {ButtonShadow}, "
f"{ButtonHilight}, {ButtonFace}, {ButtonText})"
)
args = [
mogrify_path,
'-fill', ButtonDKShadow, '-opaque', originals['ButtonDKShadow'],
'-fill', ButtonLight, '-opaque', originals['ButtonLight'],
'-fill', ButtonShadow, '-opaque', originals['ButtonShadow'],
'-fill', ButtonHilight, '-opaque', originals['ButtonHilight'],
'-fill', ButtonFace, '-opaque', originals['ButtonFace'],
'-fill', ButtonText, '-opaque', originals['ButtonText'],
'-quiet',
sub_asset_path
]
try:
subprocess.check_call(args)
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to process file {sub_asset_path}: {e}")
else:
self.logger.warning(f"Skipping non-file in directory: {sub_asset_path}")
continue # Skip further processing of the directory itself
# Process regular files in the current folder
if not asset.startswith("status") and not asset.startswith("branding"):
self.logger.debug(
f"mogrifying {asset} ({ButtonDKShadow}, {ButtonLight}, {ButtonShadow}, "
f"{ButtonHilight}, {ButtonFace}, {ButtonText})"
)
args = [
mogrify_path,
'-fill', ButtonDKShadow, '-opaque', originals['ButtonDKShadow'],
'-fill', ButtonLight, '-opaque', originals['ButtonLight'],
'-fill', ButtonShadow, '-opaque', originals['ButtonShadow'],
'-fill', ButtonHilight, '-opaque', originals['ButtonHilight'],
'-fill', ButtonFace, '-opaque', originals['ButtonFace'],
'-fill', ButtonText, '-opaque', originals['ButtonText'],
'-quiet',
folder+asset
mogrify_path,
'-fill', ButtonDKShadow, '-opaque', originals['ButtonDKShadow'],
'-fill', ButtonLight, '-opaque', originals['ButtonLight'],
'-fill', ButtonShadow, '-opaque', originals['ButtonShadow'],
'-fill', ButtonHilight, '-opaque', originals['ButtonHilight'],
'-fill', ButtonFace, '-opaque', originals['ButtonFace'],
'-fill', ButtonText, '-opaque', originals['ButtonText'],
'-quiet',
asset_path
]
subprocess.check_call(args)
try:
subprocess.check_call(args)
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to process file {asset_path}: {e}")
#### Icon/Cursor Functions
@ -2943,33 +2989,91 @@ class ChicagoPlus:
shutil.rmtree(install_theme_dir, ignore_errors=True)
shutil.copytree(self.folder_names['sounds'],install_theme_dir,symlinks=True,ignore_dangling_symlinks=True)
def install_wallpaper(self, os_wallpaper_dir=str(Path.home())+"/Pictures/"):
self.logger.info("Installing wallpaper")
def install_wallpaper(self, os_wallpaper_dir=str(Path.home()) + "/Pictures/"):
"""
Install the wallpaper by copying it to the designated directory and applying it
to all monitors and workspaces dynamically.
"""
self.logger.info("Installing and applying wallpaper.")
# Ensure the destination directory exists
if not os.path.exists(os_wallpaper_dir):
self.logger.error("Theme install directory does not exists: {}".format(os_wallpaper_dir))
self.logger.error(f"Theme install directory does not exist: {os_wallpaper_dir}")
return
if (self.theme_config['wallpaper']['theme_wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['path']):
self.logger.debug("Copying {} to {}".format(self.theme_config['wallpaper']['theme_wallpaper']['path'], os_wallpaper_dir + self.theme_config['wallpaper']['theme_wallpaper']['new_filename']))
try:
shutil.copy(self.theme_config['wallpaper']['theme_wallpaper']['path'], os_wallpaper_dir + self.theme_config['wallpaper']['theme_wallpaper']['new_filename'])
except:
self.logger.error("Could not install wallpaper to {}".format(os_wallpaper_dir))
# Install the main wallpaper
if (self.theme_config['wallpaper']['theme_wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['path']):
wallpaper_path = os.path.join(os_wallpaper_dir, self.theme_config['wallpaper']['theme_wallpaper']['new_filename'])
self.logger.debug(f"Copying wallpaper from {self.theme_config['wallpaper']['theme_wallpaper']['path']} to {wallpaper_path}")
try:
shutil.copy(self.theme_config['wallpaper']['theme_wallpaper']['path'], wallpaper_path)
self.logger.info(f"Wallpaper successfully installed to {wallpaper_path}")
except Exception as e:
self.logger.error(f"Could not install wallpaper to {wallpaper_path}: {e}")
return
# Apply the wallpaper to all monitors and workspaces
self.apply_wallpaper_to_all_monitors(wallpaper_path, self.theme_config['wallpaper']['theme_wallpaper']['wallpaperstyle'])
# Install additional wallpapers
if self.theme_config['wallpaper']['extra_wallpapers']:
for wallpaper in self.theme_config['wallpaper']['extra_wallpapers']:
self.logger.debug("Copying {} to {}".format(wallpaper, os_wallpaper_dir))
try:
shutil.copy(wallpaper, os_wallpaper_dir)
except:
self.logger.error("Could not install wallpaper to {}".format(wallpaper))
raise
dest = os.path.join(os_wallpaper_dir, os.path.basename(wallpaper))
self.logger.debug(f"Copying extra wallpaper from {wallpaper} to {dest}")
try:
shutil.copy(wallpaper, dest)
self.logger.info(f"Extra wallpaper successfully installed to {dest}")
except Exception as e:
self.logger.error(f"Could not install extra wallpaper to {dest}: {e}")
def apply_wallpaper_to_all_monitors(self, wallpaper_path, wallpaper_style):
"""
Apply the wallpaper to all monitors and workspaces dynamically.
Args:
wallpaper_path (str): Path to the wallpaper image.
wallpaper_style (int): Wallpaper style (e.g., 0 = centered, 2 = stretched).
"""
channel = "xfce4-desktop"
self.logger.info(f"Applying wallpaper '{wallpaper_path}' to all monitors and workspaces.")
try:
xfconf_query_path = subprocess.check_output(["which", "xfconf-query"]).strip()
properties = subprocess.check_output(
[xfconf_query_path, "--channel", channel, "--list"],
universal_newlines=True
).splitlines()
# Filter relevant properties for workspaces and monitors
for prop in properties:
if "workspace" in prop and "last-image" in prop:
workspace_base = prop.rsplit("/", 1)[0] # Get base workspace path
last_image_property = f"{workspace_base}/last-image"
image_style_property = f"{workspace_base}/image-style"
# Set wallpaper
self.logger.debug(f"Setting wallpaper for {last_image_property} to {wallpaper_path}")
subprocess.check_call(
[xfconf_query_path, "--channel", channel, "--property", last_image_property, "--set", wallpaper_path]
)
# Set wallpaper style
self.logger.debug(f"Setting image style for {image_style_property} to {wallpaper_style}")
subprocess.check_call(
[xfconf_query_path, "--channel", channel, "--property", image_style_property, "--set", str(wallpaper_style)]
)
self.logger.info("Wallpaper applied successfully.")
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to set wallpaper properties: {e}")
except Exception as e:
self.logger.error(f"Unexpected error while applying wallpaper: {e}")
## Enable the theme in XFCE
## Enable the theme in XFCE
def enable_theme(self, cursors=True, icons=True, wallpaper=True, sounds=True, colors=True, fonts=True, screensaver=True):
self.logger.info("Enabling {}".format(self.theme_name))
@ -3013,8 +3117,6 @@ class ChicagoPlus:
self.xfconf_query('xfce4-desktop', '/backdrop/screen0/monitor0/workspace0/image-style', "4")
else:
self.logger.debug("Wallpaper failed to install")
if sounds:
self.logger.info("Enabling New Sounds")
@ -3025,6 +3127,8 @@ class ChicagoPlus:
self.xfconf_query('xsettings', '/Net/ThemeName', self.theme_name+"_Theme")
if screensaver:
self.logger.info("Screensavers require manual install. See the script in {}".format(self.folder_names['screensaver']))
self.logger.info("Theme installation completed successfully!")
def enable_fonts(self):
@ -3147,19 +3251,47 @@ class ChicagoPlus:
self.xfconf_query('xfwm4', '/general/title_font', font + ' 8')
## Enable Helper functions
def xfconf_query(self, channel, prop, new_value):
def xfconf_query(self, channel, prop_base, new_value):
"""
Dynamically sets xfconf properties for all monitors and workspaces.
:param channel: The xfconf channel (e.g., "xfce4-desktop").
:param prop_base: Base property path (e.g., "/backdrop/screen0").
:param new_value: The new value to set for the property.
"""
try:
xfconf_query_path = subprocess.check_output(["which", "xfconf-query"]).strip()
self.logger.debug("Changing xfconf setting {}/{} to {}".format(channel, prop, new_value))
args = [
xfconf_query_path,
"--channel", channel,
"--property", prop,
"--set", new_value
]
subprocess.check_call(args, stdout=subprocess.DEVNULL)
self.logger.debug(f"Changing xfconf setting {channel}/{prop_base} to {new_value}")
# Retrieve all properties in the channel
props = subprocess.check_output(
[xfconf_query_path, "--channel", channel, "--list"],
universal_newlines=True
).splitlines()
# Filter relevant properties for monitors and workspaces
for prop in props:
if prop.startswith(prop_base):
self.logger.debug(f"Found property: {prop}")
# Update the property value
args = [
xfconf_query_path,
"--channel", channel,
"--property", prop,
"--set", new_value
]
try:
subprocess.check_call(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.logger.info(f"Set {prop} to {new_value}")
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to set {prop}: {e}")
except subprocess.CalledProcessError:
self.logger.info("xfconf not installed, enable theme manually")
self.logger.error("xfconf-query not installed or not available. Enable theme manually.")
except Exception as e:
self.logger.error(f"Error setting xfconf property: {e}")
def get_font_list(self):
fc_list = subprocess.check_output(["which", "fc-list"]).strip()