Source code for ttkthemes._widget

"""
Author: RedFantom
License: GNU GPLv3
Copyright (c) 2017-2018 RedFantom
"""
# Standard library
import os
from shutil import copytree, rmtree
# Packages
from PIL import Image, ImageEnhance
# Project Modules
from . import _utils as utils
from . import _imgops as imgops
from ._tkinter import tk
from ._utils import get_file_directory


[docs]class ThemedWidget(object): """ Provides functions to manipulate themes in order to reduce code duplication in the ThemedTk and ThemedStyle classes. """ pixmap_themes = [ "arc", "blue", "clearlooks", "elegance", "kroc", "plastik", "radiance", "ubuntu", "winxpblue" ]
[docs] def __init__(self, tk_interpreter, gif_override=False): """ Initialize attributes and call _load_themes :param tk_interpreter: tk interpreter for tk.Widget that is being initialized as ThemedWidget. Even if this Widget is just a single widget, the changes affect all widgets with the same parent Tk instance. :param gif_override: Force loading of GIF-themes even if PNG-themes can be loaded """ self.tk = tk_interpreter # Change working directory temporarily to allow Tcl to work self.png_support = True and not gif_override if tk.TkVersion <= 8.5 and not gif_override: self.png_support = False try: from tkimg import load_tkimg_into_interpreter load_tkimg_into_interpreter(self.tk) self.png_support = True except (ImportError, tk.TclError): pass self._load_themes()
def _load_themes(self): """Load the themes into the Tkinter interpreter""" with utils.temporary_chdir(utils.get_file_directory()): self._append_theme_dir("themes") self.tk.eval("source themes/pkgIndex.tcl") theme_dir = "gif" if not self.png_support else "png" self._append_theme_dir(theme_dir) self.tk.eval("source {}/pkgIndex.tcl".format(theme_dir)) def _append_theme_dir(self, name): """Append a theme dir to the Tk interpreter auto_path""" path = "[{}]".format(get_file_directory() + "/" + name) self.tk.call("lappend", "auto_path", path)
[docs] def set_theme(self, theme_name): """ Set new theme to use. Uses a direct tk call to allow usage of the themes supplied with this package. :param theme_name: name of theme to activate """ self.tk.call("package", "require", "ttkthemes") self.tk.call("ttk::setTheme", theme_name)
[docs] def get_themes(self): """Return a list of names of available themes""" self.tk.call("package", "require", "ttkthemes") return list(self.tk.call("ttk::themes"))
@property def themes(self): """Property alias of get_themes()""" return self.get_themes()
[docs] def set_theme_advanced(self, theme_name, brightness=1.0, saturation=1.0, hue=1.0, preserve_transparency=True, output_dir=None, advanced_name="advanced"): """ Load an advanced theme that is dynamically created Applies the given modifiers to the images of the theme given and then creates a theme from these new images with the name 'advanced' and then applies this theme. Is not available without support for PNG-based themes, then raises RuntimeError. """ if not self.png_support: raise RuntimeError("PNG-based themes are not supported in the environment") # Check if the theme is a pixmap theme if theme_name not in self.pixmap_themes: raise ValueError("Theme is not a valid pixmap theme") # Check if theme is available in the first place if theme_name not in self.themes: raise ValueError("Theme to create new theme from is not available: {}".format(theme_name)) if advanced_name in self.themes: raise RuntimeError("The same name for an advanced theme cannot be used twice") # Unload advanced if already loaded output_dir = os.path.join(utils.get_temp_directory(), advanced_name) if output_dir is None else output_dir self._setup_advanced_theme(theme_name, output_dir, advanced_name) # Perform image operations image_directory = os.path.join(output_dir, advanced_name, advanced_name) self._setup_images(image_directory, brightness, saturation, hue, preserve_transparency) # Load the new theme with utils.temporary_chdir(output_dir): self.tk.call("lappend", "auto_path", "[{}]".format(output_dir)) self.tk.eval("source pkgIndex.tcl") self.set_theme(advanced_name)
def _setup_advanced_theme(self, theme_name, output_dir, advanced_name): """ Setup all the files required to enable an advanced theme. Copies all the files over and creates the required directories if they do not exist. :param theme_name: theme to copy the files over from :param output_dir: output directory to place the files in """ """Directories""" output_theme_dir = os.path.join(output_dir, advanced_name) output_images_dir = os.path.join(output_theme_dir, advanced_name) input_theme_dir = os.path.join( utils.get_themes_directory(theme_name, self.png_support), theme_name) input_images_dir = os.path.join(input_theme_dir, theme_name) advanced_pkg_dir = os.path.join(utils.get_file_directory(), "advanced") """Directory creation""" for directory in [output_dir, output_theme_dir]: utils.create_directory(directory) """Theme TCL file""" file_name = theme_name + ".tcl" theme_input = os.path.join(input_theme_dir, file_name) theme_output = os.path.join(output_theme_dir, "{}.tcl".format(advanced_name)) with open(theme_input, "r") as fi, open(theme_output, "w") as fo: for line in fi: # Setup new theme line = line.replace(theme_name, advanced_name) # Setup new image format line = line.replace("gif89", "png") line = line.replace("gif", "png") # Write processed line fo.write(line) """pkgIndex.tcl file""" theme_pkg_input = os.path.join(advanced_pkg_dir, "pkgIndex.tcl") theme_pkg_output = os.path.join(output_theme_dir, "pkgIndex.tcl") with open(theme_pkg_input, "r") as fi, open(theme_pkg_output, "w") as fo: for line in fi: fo.write(line.replace("advanced", advanced_name)) """pkgIndex_package.tcl -> pkgIndex.tcl""" theme_pkg_input = os.path.join(advanced_pkg_dir, "pkgIndex_package.tcl") theme_pkg_output = os.path.join(output_dir, "pkgIndex.tcl") with open(theme_pkg_input, "r") as fi, open(theme_pkg_output, "w") as fo: for line in fi: fo.write(line.replace("advanced", advanced_name)) """Images""" if os.path.exists(output_images_dir): rmtree(output_images_dir) copytree(input_images_dir, output_images_dir) @staticmethod def _setup_images(directory, brightness, saturation, hue, preserve_transparency): """ Apply modifiers to the images of a theme Modifies the images using the PIL.ImageEnhance module. Using this function, theme images are modified to given them a unique look and feel. Works best with PNG-based images. """ for file_name in os.listdir(directory): with open(os.path.join(directory, file_name), "rb") as fi: image = Image.open(fi).convert("RGBA") # Only perform required operations if brightness != 1.0: enhancer = ImageEnhance.Brightness(image) image = enhancer.enhance(brightness) if saturation != 1.0: enhancer = ImageEnhance.Color(image) image = enhancer.enhance(saturation) if hue != 1.0: image = imgops.shift_hue(image, hue) if preserve_transparency is True: image = imgops.make_transparent(image) # Save the new image image.save(os.path.join(directory, file_name.replace("gif", "png"))) image.close() for file_name in (item for item in os.listdir(directory) if item.endswith(".gif")): os.remove(os.path.join(directory, file_name))