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,7 +885,7 @@ class ChicagoPlus:
os.mkdir(self.folder_names[i]) os.mkdir(self.folder_names[i])
def create_icons(self, create_48_document_icon = True): 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 = {} svg_file_names = {}
png_file_names = { png_file_names = {
@ -897,102 +897,109 @@ class ChicagoPlus:
} }
for i in png_file_names: 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']: 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]: 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 continue
icon_sizes = [16,22,24,32,48] icon_sizes = [16, 22, 24, 32, 48]
filename = self.theme_config['icons'][iconname]['filename'] filename = self.theme_config['icons'][iconname]['filename']
index = self.theme_config['icons'][iconname]['index'] index = self.theme_config['icons'][iconname]['index']
path = self.theme_config['icons'][iconname]['path'] path = self.theme_config['icons'][iconname]['path']
filetype = self.theme_config['icons'][iconname]['type'] filetype = self.theme_config['icons'][iconname]['type']
if not path: self.logger.debug(f"Icon details - Name: {filename}, Index: {index}, Path: {path}, Type: {filetype}")
self.logger.error("{:<21} | {} does not exist in this theme".format(iconname, filename))
continue
if not path:
self.logger.warning(f"{iconname:<21} | {filename} does not exist in this theme")
continue
if filetype not in ['dll', 'icl', 'ico', 'bmp']: if filetype not in ['dll', 'icl', 'ico', 'bmp']:
# wut self.logger.warning(f"File type {filetype} not supported: {filename}")
self.logger.error("File type {} not supported: {}".format(filetype, filename))
continue continue
self.logger.info("{:<21} | {}".format(iconname, filename)) self.logger.info(f"{iconname:<21} | {filename}")
if filetype in ['dll', 'icl']: if filetype in ['dll', 'icl']:
# Get the icons stored in the DLL # Get the icons stored in the DLL
self.logger.debug("{:<21} | Icons are stored in ICL file {}".format("", filename, path)) self.logger.debug("{:<21} | Icons are stored in ICL file {}".format("", filename, path))
icon_files = self.extract_icons_from_dll(path) icon_files = self.extract_icons_from_dll(path)
else: 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) icon_files = self.extract_ico(path)
if icon_files == 'bmp': if icon_files == 'bmp':
filetype = 'bmp' filetype = 'bmp'
if not icon_files: 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 continue
# If the icons exist at various sizes, write them and convert them instead of scaling the largest one # If the icons exist at various sizes, write them and convert them instead of scaling the largest one
for size in icon_sizes: 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) 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) icon_filename, icon_file = self.get_icons_size_ico(icon_files, size)
else: else:
icon_filename = False icon_filename = False
if icon_filename: if icon_filename:
icon_sizes.remove(size) icon_sizes.remove(size)
f = open(self.folder_names['icons']+icon_filename,"wb") destination = os.path.join(self.folder_names['icons'], icon_filename)
with open(destination, "wb") as f:
f.write(icon_file) f.write(icon_file)
f.close() sized_target = os.path.join(
sized_target = self.folder_names['icons']+"places/"+str(size)+"/"+png_file_names[iconname] 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) 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 # Now that we're done, get the largest file and use that for the rest
if filetype in ['dll', 'icl']: if filetype in ['dll', 'icl']:
icon_filename, icon_file = self.get_largest_icon_dll(icon_files, index) 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) icon_filename, icon_file = self.get_largest_icon_ico(icon_files, size)
if filetype in ['dll', 'icl', 'ico'] and not isinstance(icon_file, str): if filetype in ['dll', 'icl', 'ico'] and not isinstance(icon_file, str):
f = open(self.folder_names['icons']+icon_filename,"wb") destination = os.path.join(self.folder_names['icons'], icon_filename)
with open(destination, "wb") as f:
f.write(icon_file) f.write(icon_file)
f.close()
else: else:
shutil.copyfile(path, self.folder_names['icons']+icon_filename) destination = os.path.join(self.folder_names['icons'], os.path.basename(icon_filename))
shutil.copyfile(path, destination)
svg_icon_file = self.convert_icon(self.folder_names['icons'], destination)
svg_icon_file = self.convert_icon(self.folder_names['icons'], self.folder_names['icons']+icon_filename)
for size in icon_sizes: for size in icon_sizes:
if size <= 32 and iconname == "documents_ico" and not create_48_document_icon: if size <= 32 and iconname == "documents_ico" and not create_48_document_icon:
continue continue
sized_target = self.folder_names['icons']+"places/"+str(size)+"/"+png_file_names[iconname] sized_target = os.path.join(
self.logger.debug("{:<21} | Creating: {} {} {}".format("", size, svg_icon_file, sized_target)) self.folder_names['icons'], "places", str(size), png_file_names[iconname]
self.convert_to_png_with_inkscape( svg_icon_file, size, sized_target) )
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 = self.folder_names['icons']+"places/scalable/"+svg_file_names[iconname] scaled_target = os.path.join(
self.folder_names['icons'], "places", "scalable", svg_file_names[iconname]
)
shutil.copy(svg_icon_file, scaled_target) shutil.copy(svg_icon_file, scaled_target)
# Update index.theme
self.logger.debug("Updating icon index.theme file") self.logger.debug("Updating icon index.theme file")
icon_theme_config = configparser.RawConfigParser(interpolation=None) icon_theme_config = configparser.RawConfigParser(interpolation=None)
icon_theme_config.optionxform = str icon_theme_config.optionxform = str
icon_theme_config.read(self.folder_names['icons']+"/index.theme") icon_theme_config.read(os.path.join(self.folder_names['icons'], "index.theme"))
icon_theme_config.set("Icon Theme","Name",self.index_theme_name) icon_theme_config.set("Icon Theme", "Name", self.index_theme_name)
with open(self.folder_names['icons']+"/index.theme", 'w') as configfile: with open(os.path.join(self.folder_names['icons'], "index.theme"), 'w') as configfile:
icon_theme_config.write(configfile, space_around_delimiters=False) icon_theme_config.write(configfile, space_around_delimiters=False)
def create_cursors(self): def create_cursors(self):
self.logger.info("Creating new xcursors in {}".format(self.folder_names['cursors'])) self.logger.info("Creating new xcursors in {}".format(self.folder_names['cursors']))
pointers = { pointers = {
@ -1799,13 +1806,22 @@ class ChicagoPlus:
} }
for i in ['gtk-3.0/assets/','gtk-3.24/assets/','gtk-3.0/scrollbar/','gtk-3.24/scrollbar/']: for i in ['gtk-3.0/assets/', 'gtk-3.24/assets/', 'gtk-3.0/scrollbar/', 'gtk-3.24/scrollbar/']:
folder = path + i folder = path + i
for asset in os.listdir(folder): for asset in os.listdir(folder):
if not asset.startswith("status") and not asset.startswith("branding"): asset_path = os.path.join(folder, asset) # Get full path of the asset
self.logger.debug("mogrifying {} ({} {} {} {} {} {})".format(asset, ButtonDKShadow, ButtonLight, ButtonShadow, ButtonHilight, ButtonFace, ButtonText))
# 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 = [ args = [
mogrify_path, mogrify_path,
'-fill', ButtonDKShadow, '-opaque', originals['ButtonDKShadow'], '-fill', ButtonDKShadow, '-opaque', originals['ButtonDKShadow'],
@ -1815,9 +1831,39 @@ class ChicagoPlus:
'-fill', ButtonFace, '-opaque', originals['ButtonFace'], '-fill', ButtonFace, '-opaque', originals['ButtonFace'],
'-fill', ButtonText, '-opaque', originals['ButtonText'], '-fill', ButtonText, '-opaque', originals['ButtonText'],
'-quiet', '-quiet',
folder+asset sub_asset_path
] ]
try:
subprocess.check_call(args) 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',
asset_path
]
try:
subprocess.check_call(args)
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to process file {asset_path}: {e}")
#### Icon/Cursor Functions #### Icon/Cursor Functions
@ -2943,33 +2989,91 @@ class ChicagoPlus:
shutil.rmtree(install_theme_dir, ignore_errors=True) shutil.rmtree(install_theme_dir, ignore_errors=True)
shutil.copytree(self.folder_names['sounds'],install_theme_dir,symlinks=True,ignore_dangling_symlinks=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/"):
"""
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.")
def install_wallpaper(self, os_wallpaper_dir=str(Path.home())+"/Pictures/"): # Ensure the destination directory exists
self.logger.info("Installing wallpaper")
if not os.path.exists(os_wallpaper_dir): 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 return
# Install the main wallpaper
if (self.theme_config['wallpaper']['theme_wallpaper'] and if (self.theme_config['wallpaper']['theme_wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['wallpaper'] and self.theme_config['wallpaper']['theme_wallpaper']['wallpaper'] and
self.theme_config['wallpaper']['theme_wallpaper']['path']): 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))
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']: if self.theme_config['wallpaper']['extra_wallpapers']:
for wallpaper in 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)) dest = os.path.join(os_wallpaper_dir, os.path.basename(wallpaper))
self.logger.debug(f"Copying extra wallpaper from {wallpaper} to {dest}")
try: try:
shutil.copy(wallpaper, os_wallpaper_dir) shutil.copy(wallpaper, dest)
except: self.logger.info(f"Extra wallpaper successfully installed to {dest}")
self.logger.error("Could not install wallpaper to {}".format(wallpaper)) except Exception as e:
raise self.logger.error(f"Could not install extra wallpaper to {dest}: {e}")
## Enable the theme in XFCE 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
def enable_theme(self, cursors=True, icons=True, wallpaper=True, sounds=True, colors=True, fonts=True, screensaver=True): 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)) self.logger.info("Enabling {}".format(self.theme_name))
@ -3014,8 +3118,6 @@ class ChicagoPlus:
else: else:
self.logger.debug("Wallpaper failed to install") self.logger.debug("Wallpaper failed to install")
if sounds: if sounds:
self.logger.info("Enabling New Sounds") self.logger.info("Enabling New Sounds")
self.xfconf_query('xsettings', '/Net/SoundThemeName', self.theme_name+"_Sounds") self.xfconf_query('xsettings', '/Net/SoundThemeName', self.theme_name+"_Sounds")
@ -3026,6 +3128,8 @@ class ChicagoPlus:
if screensaver: if screensaver:
self.logger.info("Screensavers require manual install. See the script in {}".format(self.folder_names['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): def enable_fonts(self):
# /Gtk/FontName "Family name and size" <- this is used for the other fonts, not titlebars # /Gtk/FontName "Family name and size" <- this is used for the other fonts, not titlebars
@ -3147,19 +3251,47 @@ class ChicagoPlus:
self.xfconf_query('xfwm4', '/general/title_font', font + ' 8') self.xfconf_query('xfwm4', '/general/title_font', font + ' 8')
## Enable Helper functions ## 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: try:
xfconf_query_path = subprocess.check_output(["which", "xfconf-query"]).strip() xfconf_query_path = subprocess.check_output(["which", "xfconf-query"]).strip()
self.logger.debug("Changing xfconf setting {}/{} to {}".format(channel, prop, new_value)) 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 = [ args = [
xfconf_query_path, xfconf_query_path,
"--channel", channel, "--channel", channel,
"--property", prop, "--property", prop,
"--set", new_value "--set", new_value
] ]
subprocess.check_call(args, stdout=subprocess.DEVNULL) 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: 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): def get_font_list(self):
fc_list = subprocess.check_output(["which", "fc-list"]).strip() fc_list = subprocess.check_output(["which", "fc-list"]).strip()