-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_widget.py
More file actions
216 lines (194 loc) · 8.76 KB
/
_widget.py
File metadata and controls
216 lines (194 loc) · 8.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
"""
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 ._utils import get_file_directory
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"
]
PACKAGES = {
"keramik_alt": "keramik",
"scidblue": "scid",
"scidgreen": "scid",
"scidgrey": "scid",
"scidmint": "scid",
"scidpink": "scid",
"scidpurple": "scid",
"scidsand": "scid",
}
_EXCLUDED = {"scid"}
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 = not gif_override
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))
self.tk.call("package", "require", "ttk::theme::scid")
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)
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
"""
package = theme_name if theme_name not in self.PACKAGES else self.PACKAGES[theme_name]
self.tk.call("package", "require", "ttk::theme::{}".format(package))
self.tk.call("ttk::setTheme", theme_name)
def get_themes(self):
"""Return a list of names of available themes"""
return list(set(self.tk.call("ttk::themes")) - self._EXCLUDED)
@property
def themes(self):
"""Property alias of get_themes()"""
return self.get_themes()
@property
def current_theme(self):
"""Property to get the currently enabled theme"""
return self.tk.eval("return $ttk::currentTheme")
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))