forked from EDCD/EDMarketConnector
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththeme.py
207 lines (180 loc) · 8.87 KB
/
theme.py
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
#
# Theme support
#
# Because of various ttk limitations this app is an unholy mix of Tk and ttk widgets.
# So can't use ttk's theme support. So have to change colors manually.
#
from sys import platform
import Tkinter as tk
import ttk
import tkFont
from config import appname, applongname, config
class _Theme:
def __init__(self):
self.active = None # Starts out with no theme
self.minwidth = None
self.widgets = set()
self.widgets_pair = []
def register(self, widget):
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
for child in widget.winfo_children():
self.register(child)
self.widgets.add(widget)
def register_alternate(self, pair, gridopts):
self.widgets_pair.append((pair, gridopts))
def button_bind(self, widget, command, image=None):
widget.bind('<Button-1>', command)
widget.bind('<Enter>', lambda e: self._enter(e, image))
widget.bind('<Leave>', lambda e: self._leave(e, image))
def _enter(self, event, image):
widget = event.widget
if widget and widget['state'] != tk.DISABLED:
widget.configure(state = tk.ACTIVE)
if image:
image.configure(foreground = self.current['activeforeground'], background = self.current['activebackground'])
def _leave(self, event, image):
widget = event.widget
if widget and widget['state'] != tk.DISABLED:
widget.configure(state = tk.NORMAL)
if image:
image.configure(foreground = self.current['foreground'], background = self.current['background'])
# Set up colors
def _colors(self, root, theme):
style = ttk.Style()
if platform == 'linux2':
style.theme_use('clam')
elif platform == 'darwin':
# Default ttk font spacing looks bad on El Capitan
osxfont = tkFont.Font(family='TkDefaultFont', size=13, weight=tkFont.NORMAL)
style.configure('TLabel', font=osxfont)
style.configure('TButton', font=osxfont)
style.configure('TLabelframe.Label', font=osxfont)
style.configure('TCheckbutton', font=osxfont)
style.configure('TRadiobutton', font=osxfont)
style.configure('TEntry', font=osxfont)
# Default dark theme colors
if not config.get('dark_text'):
config.set('dark_text', '#ff8000') # "Tangerine" in OSX color picker
if not config.get('dark_highlight'):
config.set('dark_highlight', 'white')
if theme:
# Dark
(r, g, b) = root.winfo_rgb(config.get('dark_text'))
self.current = {
'background' : 'grey4', # OSX inactive dark titlebar color
'foreground' : config.get('dark_text'),
'activebackground' : config.get('dark_text'),
'activeforeground' : 'grey4',
'disabledforeground' : '#%02x%02x%02x' % (r/384, g/384, b/384),
'highlight' : config.get('dark_highlight'),
'font' : 'TkDefaultFont',
}
# Overrides
if platform == 'darwin':
self.current['font'] = osxfont
else:
# System colors
self.current = {
'background' : style.lookup('TLabel', 'background'),
'foreground' : style.lookup('TLabel', 'foreground'),
'activebackground' : style.lookup('TLabel', 'background', ['active']),
'activeforeground' : style.lookup('TLabel', 'foreground', ['active']),
'disabledforeground' : style.lookup('TLabel', 'foreground', ['disabled']),
'highlight' : 'blue',
'font' : 'TkDefaultFont',
}
# Overrides
if platform == 'darwin':
self.current['background'] = 'systemMovableModalBackground'
self.current['font'] = osxfont
elif platform == 'win32':
# Menu colors
self.current['activebackground'] = 'SystemHighlight'
self.current['activeforeground'] = 'SystemHighlightText'
# Apply configured theme
def apply(self, root):
theme = config.getint('theme')
self._colors(root, theme)
# Apply colors
for widget in self.widgets:
if isinstance(widget, tk.BitmapImage):
# not a widget
widget.configure(foreground = self.current['foreground'],
background = self.current['background'])
elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']:
# Hack - highlight widgets like HyperlinkLabel with a non-default cursor
widget.configure(foreground = self.current['highlight'],
background = self.current['background'])
elif 'activeforeground' in widget.keys():
# e.g. tk.Button, tk.Label, tk.Menu
widget.configure(foreground = self.current['foreground'],
background = self.current['background'],
activeforeground = self.current['activeforeground'],
activebackground = self.current['activebackground'],
disabledforeground = self.current['disabledforeground'],
font = self.current['font']
)
elif 'foreground' in widget.keys():
# e.g. ttk.Label
widget.configure(foreground = self.current['foreground'],
background = self.current['background'],
font = self.current['font'])
elif 'background' in widget.keys():
# e.g. Frame
widget.configure(background = self.current['background'])
widget.configure(highlightbackground = self.current['disabledforeground'])
for pair, gridopts in self.widgets_pair:
for widget in pair:
widget.grid_remove()
if isinstance(pair[0], tk.Menu):
if theme:
root['menu'] = ''
pair[theme].grid(**gridopts)
else:
root['menu'] = pair[0]
else:
pair[theme].grid(**gridopts)
if self.active == theme:
return # Don't need to mess with the window manager
else:
self.active = theme
if platform == 'darwin':
from AppKit import NSApplication, NSAppearance, NSMiniaturizableWindowMask, NSResizableWindowMask
root.update_idletasks() # need main window to be created
appearance = NSAppearance.appearanceNamed_(theme and
'NSAppearanceNameVibrantDark' or
'NSAppearanceNameAqua')
for window in NSApplication.sharedApplication().windows():
window.setStyleMask_(window.styleMask() & ~(NSMiniaturizableWindowMask | NSResizableWindowMask)) # disable zoom
window.setAppearance_(appearance)
elif platform == 'win32':
import ctypes
GWL_STYLE = -16
WS_MAXIMIZEBOX = 0x00010000
# tk8.5.9/win/tkWinWm.c:342
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_LAYERED = 0x00080000
GetWindowLongW = ctypes.windll.user32.GetWindowLongW
SetWindowLongW = ctypes.windll.user32.SetWindowLongW
root.overrideredirect(theme and 1 or 0)
root.attributes("-transparentcolor", theme > 1 and 'grey4' or '')
root.withdraw()
root.update_idletasks() # Size and windows styles get recalculated here
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize
SetWindowLongW(hwnd, GWL_EXSTYLE, theme > 1 and WS_EX_APPWINDOW|WS_EX_LAYERED or WS_EX_APPWINDOW) # Add to taskbar
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
else:
root.overrideredirect(theme and 1 or 0)
root.withdraw()
root.update_idletasks() # Size gets recalculated here
root.deiconify()
root.wait_visibility() # need main window to be displayed before returning
if not self.minwidth:
self.minwidth = root.winfo_width() # Minimum width = width on first creation
root.minsize(self.minwidth, -1)
# singleton
theme = _Theme()