1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-13 07:47:14 +03:00

Dark theme

Fixes #84
This commit is contained in:
Jonathan Harris 2016-02-01 03:38:55 +00:00
parent 0b2ed9614d
commit f0683926fd
18 changed files with 598 additions and 70 deletions

View File

@ -43,6 +43,7 @@ import plug
from edproxy import edproxy
from hotkey import hotkeymgr
from monitor import monitor
from theme import theme
EDDB = eddb.EDDB()
@ -65,62 +66,65 @@ class AppWindow:
self.w.rowconfigure(0, weight=1)
self.w.columnconfigure(0, weight=1)
# Special handling for overrideredict
self.w.bind("<Map>", self.onmap)
plug.load_plugins()
if platform == 'win32':
self.w.wm_iconbitmap(default='EDMarketConnector.ico')
elif platform == 'linux2':
from PIL import Image, ImageTk
icon = ImageTk.PhotoImage(Image.open("EDMarketConnector.png"))
self.w.tk.call('wm', 'iconphoto', self.w, '-default', icon)
style = ttk.Style()
style.theme_use('clam')
elif platform=='darwin':
# Default ttk font choice looks bad on El Capitan
font = tkFont.Font(family='TkDefaultFont', size=13, weight=tkFont.NORMAL)
style = ttk.Style()
style.configure('TLabel', font=font)
style.configure('TButton', font=font)
style.configure('TLabelframe.Label', font=font)
style.configure('TCheckbutton', font=font)
style.configure('TRadiobutton', font=font)
style.configure('TEntry', font=font)
if platform != 'darwin':
if platform == 'win32':
self.w.wm_iconbitmap(default='EDMarketConnector.ico')
else:
from PIL import Image, ImageTk
self.w.tk.call('wm', 'iconphoto', self.w, '-default', ImageTk.PhotoImage(Image.open("EDMarketConnector.png")))
self.theme_icon = tk.PhotoImage(data = 'R0lGODlhEAAQAMYAAAAAAAEAAAEBAQICAgQEAwYFBAsHBAoIBgwIBAwIBQ0IBA8JBBAJBBAKBRMMBRkPBhoQBykWCSoWCCoXCTsfCzwfCkAhDEIjDD8kC0AlDEEmC0EmDEcoDk4oDU8pDU4qEFMrD1ktDlotD1ouD1g0EWAyEWU0EV03EmA4EWo2EW03EWQ6Emw4EWo9FGo+E3Y8FH5AFH1IFoBJFo1RGo1SGY1SGpBTGZFTGZJTGZhYG6piHa1kHa5kHbBkHr9uIMt0IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEKAEAALAAAAAAQABAAAAd7gACCg4SFhoeHGCiIhRs5JwMCkpKGGTIFODaaNjc/D4QaMQMAk5MuEIQOO6OFAiscCIQNPQk8NTO4NDofLwayPi0mIMPDLAcqvoIBDiQWkaUCAykKhAsXAoYCHRKEDDAjIyIiIyEEHhHYhAPr7BQlE+mMABXo8oTx9oWBADs=')
self.theme_minimize = tk.BitmapImage(data = '#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f,\n 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\n')
self.theme_close = tk.BitmapImage(data = '#define im_width 16\n#define im_height 16\nstatic unsigned char im_bits[] = {\n 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x1c, 0x38, 0x38, 0x1c, 0x70, 0x0e,\n 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c,\n 0x1c, 0x38, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00 };\n')
frame = ttk.Frame(self.w, name=appname.lower())
frame = tk.Frame(self.w, name=appname.lower())
frame.grid(sticky=tk.NSEW)
frame.columnconfigure(1, weight=1)
ttk.Label(frame, text=_('Cmdr')+':').grid(row=0, column=0, sticky=tk.W) # Main window
ttk.Label(frame, text=_('System')+':').grid(row=1, column=0, sticky=tk.W) # Main window
ttk.Label(frame, text=_('Station')+':').grid(row=2, column=0, sticky=tk.W) # Main window
tk.Label(frame, text=_('Cmdr')+':').grid(row=1, column=0, sticky=tk.W) # Main window
tk.Label(frame, text=_('System')+':').grid(row=2, column=0, sticky=tk.W) # Main window
tk.Label(frame, text=_('Station')+':').grid(row=3, column=0, sticky=tk.W) # Main window
self.cmdr = ttk.Label(frame, width=-21)
self.cmdr = tk.Label(frame, anchor=tk.W)
self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url = self.system_url, popup_copy = True)
self.station = HyperlinkLabel(frame, url = self.station_url, popup_copy = lambda x: x!=self.STATION_UNDOCKED)
self.cmdr.grid(row=0, column=1, sticky=tk.EW)
self.system.grid(row=1, column=1, sticky=tk.EW)
self.station.grid(row=2, column=1, sticky=tk.EW)
self.cmdr.grid(row=1, column=1, sticky=tk.EW)
self.system.grid(row=2, column=1, sticky=tk.EW)
self.station.grid(row=3, column=1, sticky=tk.EW)
for plugname in plug.PLUGINS:
appitem = plug.get_plugin_app(plugname, frame)
if appitem:
appitem.grid(columnspan=2, sticky=tk.W)
self.button = ttk.Button(frame, name='update', text=_('Update'), command=self.getandsend, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window
self.status = ttk.Label(frame, name='status', width=-25)
self.button.grid(columnspan=2, sticky=tk.NSEW)
minwidth = platform == 'darwin' and 32 or 28
self.button = ttk.Button(frame, text=_('Update'), width=minwidth, command=self.getandsend, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window
self.theme_button = tk.Label(frame, text=_('Update'), width=minwidth, state=tk.DISABLED) # Update button in main window
self.status = tk.Label(frame, name='status', anchor=tk.W)
row = frame.grid_size()[1]
self.button.grid(row=row, columnspan=2, sticky=tk.NSEW)
self.theme_button.grid(row=row, columnspan=2, sticky=tk.NSEW)
theme.register_alternate((self.button, self.theme_button), {'row':row, 'columnspan':2, 'sticky':tk.NSEW})
self.status.grid(columnspan=2, sticky=tk.EW)
theme.button_bind(self.theme_button, self.getandsend)
self.w.bind('<Return>', self.getandsend)
self.w.bind('<KP_Enter>', self.getandsend)
for child in frame.winfo_children():
child.grid_configure(padx=5, pady=(platform=='darwin' and 3 or 2))
child.grid_configure(padx=5, pady=(platform=='win32' and 1 or 3))
menubar = tk.Menu()
if platform=='darwin':
from Foundation import NSBundle
# Can't handle (de)iconify if topmost is set, so suppress iconify button
# http://wiki.tcl.tk/13428 and p15 of https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/HandlingWindowsControls/windowscontrols.pdf
root.call('tk::unsupported::MacWindowStyle', 'style', root, 'document', 'closeBox horizontalZoom resizable')
# https://www.tcl.tk/man/tcl/TkCmd/menu.htm
apple_menu = tk.Menu(menubar, name='apple')
apple_menu.add_command(label=_("About {APP}").format(APP=applongname), command=lambda:self.w.call('tk::mac::standardAboutPanel')) # App menu entry on OSX
@ -135,6 +139,7 @@ class AppWindow:
menubar.add_cascade(label=_('View'), menu=self.view_menu) # Menu title on OSX
window_menu = tk.Menu(menubar, name='window')
menubar.add_cascade(label=_('Window'), menu=window_menu) # Menu title on OSX
self.w['menu'] = menubar
# https://www.tcl.tk/man/tcl/TkCmd/tk_mac.htm
self.w.call('set', 'tk::mac::useCompatibilityMetrics', '0')
self.w.createcommand('tkAboutDialog', lambda:self.w.call('tk::mac::standardAboutPanel'))
@ -157,34 +162,53 @@ class AppWindow:
self.always_ontop = tk.BooleanVar(value = config.getint('always_ontop'))
system_menu = tk.Menu(menubar, name='system', tearoff=tk.FALSE)
system_menu.add_separator()
system_menu.add_checkbutton(label=_('Always on top'), variable = self.always_ontop, command=self.ontop_changed) # System menu entry on Windows
system_menu.add_checkbutton(label=_('Always on top'), variable = self.always_ontop, command=self.ontop_changed) # Appearance setting
menubar.add_cascade(menu=system_menu)
self.w.wm_attributes('-topmost', self.always_ontop.get())
self.w.bind('<Control-c>', self.copy)
self.w.protocol("WM_DELETE_WINDOW", self.onexit)
theme.register(menubar) # menus and children aren't automatically registered
theme.register(file_menu)
theme.register(self.edit_menu)
if platform == 'linux2':
# Fix up menu to use same styling as everything else
(fg, bg, afg, abg) = (style.lookup('TLabel.label', 'foreground'),
style.lookup('TLabel.label', 'background'),
style.lookup('TButton.label', 'foreground', ['active']),
style.lookup('TButton.label', 'background', ['active']))
menubar.configure( fg = fg, bg = bg, activeforeground = afg, activebackground = abg)
file_menu.configure(fg = fg, bg = bg, activeforeground = afg, activebackground = abg)
self.edit_menu.configure(fg = fg, bg = bg, activeforeground = afg, activebackground = abg)
self.w['menu'] = menubar
# Alternate title bar and menu for dark theme
theme_menubar = tk.Frame(frame)
theme_menubar.columnconfigure(2, weight=1)
theme_titlebar = tk.Label(theme_menubar, text=applongname, image=self.theme_icon, anchor=tk.W, compound=tk.LEFT)
theme_titlebar.grid(columnspan=3, sticky=tk.NSEW)
self.drag_offset = None
theme_titlebar.bind('<Button-1>', self.drag_start)
theme_titlebar.bind('<B1-Motion>', self.drag_continue)
theme_titlebar.bind('<ButtonRelease-1>', self.drag_end)
if platform == 'win32': # Can't work out how to deiconify on Linux
theme_minimize = tk.Label(theme_menubar, image=self.theme_minimize)
theme_minimize.grid(row=0, column=3)
theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize)
theme_close = tk.Label(theme_menubar, image=self.theme_close)
theme_close.grid(row=0, column=4)
theme.button_bind(theme_close, self.onexit, image=self.theme_close)
theme_file_menu = tk.Label(theme_menubar, text=_('File'), anchor=tk.W) # Menu title on Windows
theme_file_menu.grid(row=1, column=0, padx=5, sticky=tk.W)
theme.button_bind(theme_file_menu, lambda e: file_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height()))
theme_edit_menu = tk.Label(theme_menubar, text=_('Edit'), anchor=tk.W) # Menu title
theme_edit_menu.grid(row=1, column=1, sticky=tk.W)
theme.button_bind(theme_edit_menu, lambda e: self.edit_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height()))
theme.register_highlight(theme_titlebar)
theme.register(self.theme_minimize) # images aren't automatically registered
theme.register(self.theme_close)
theme.register_alternate((menubar, theme_menubar), {'row':0, 'columnspan':2, 'sticky':tk.NSEW})
# update geometry
if config.get('geometry'):
match = re.match('\+([\-\d]+)\+([\-\d]+)', config.get('geometry'))
if match and (platform!='darwin' or int(match.group(2))>0): # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
self.w.geometry(config.get('geometry'))
self.w.update_idletasks()
self.w.wait_visibility()
(w, h) = (self.w.winfo_width(), self.w.winfo_height())
self.w.minsize(w, h) # Minimum size = initial size
if platform != 'linux2': # update_idletasks() doesn't allow for the menubar on Linux
self.w.maxsize(-1, h) # Maximum height = initial height
self.w.attributes('-topmost', config.getint('always_ontop') and 1 or 0)
self.w.resizable(tk.TRUE, tk.FALSE)
theme.register(frame)
theme.register_highlight(self.system)
theme.register_highlight(self.station)
theme.apply(self.w)
# Load updater after UI creation (for WinSparkle)
import update
@ -212,7 +236,7 @@ class AppWindow:
# call after credentials have changed
def login(self):
self.status['text'] = _('Logging in...')
self.button['state'] = tk.DISABLED
self.button['state'] = self.theme_button['state'] = tk.DISABLED
self.w.update_idletasks()
try:
self.session.login(config.get('username'), config.get('password'))
@ -250,7 +274,7 @@ class AppWindow:
config.save() # Save settings now for use by command-line app
except Exception as e:
if __debug__: print_exc()
self.button['state'] = tk.NORMAL
self.button['state'] = self.theme_button['state'] = tk.NORMAL
self.status['text'] = unicode(e)
else:
return self.getandsend() # try again
@ -270,7 +294,7 @@ class AppWindow:
self.cmdr['text'] = self.system['text'] = self.station['text'] = ''
self.system['image'] = ''
self.status['text'] = _('Fetching data...')
self.button['state'] = tk.DISABLED
self.theme_button['state'] = tk.DISABLED
self.edit_menu.entryconfigure(_('Copy'), state=tk.DISABLED)
self.w.update_idletasks()
@ -494,11 +518,11 @@ class AppWindow:
def cooldown(self):
if time() < self.holdofftime:
self.button['text'] = _('cooldown {SS}s').format(SS = int(self.holdofftime - time())) # Update button in main window
self.button['text'] = self.theme_button['text'] = _('cooldown {SS}s').format(SS = int(self.holdofftime - time())) # Update button in main window
self.w.after(1000, self.cooldown)
else:
self.button['text'] = _('Update') # Update button in main window
self.button['state'] = tk.NORMAL
self.button['text'] = self.theme_button['text'] = _('Update') # Update button in main window
self.button['state'] = self.theme_button['state'] = tk.NORMAL
def ontop_changed(self, event=None):
config.set('always_ontop', self.always_ontop.get())
@ -519,6 +543,27 @@ class AppWindow:
self.session.close()
self.w.destroy()
def drag_start(self, event):
self.drag_offset = (event.x_root - self.w.winfo_rootx(), event.y_root - self.w.winfo_rooty())
def drag_continue(self, event):
if self.drag_offset:
self.w.geometry('+%d+%d' % (event.x_root - self.drag_offset[0], event.y_root - self.drag_offset[1]))
def drag_end(self, event):
self.drag_offset = None
def oniconify(self, event=None):
self.w.overrideredirect(0) # Can't iconize while overrideredirect
self.w.iconify()
self.w.update_idletasks() # Size and windows styles get recalculated here
self.w.wait_visibility() # Need main window to be re-created before returning
theme.active = None # So theme will be re-applied on map
def onmap(self, event=None):
if event.widget == self.w:
theme.apply(self.w)
# Run the app
if __name__ == "__main__":

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Vždy navrchu";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API klíč";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Vzhled";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Automaticky vytvořit záznam při vstoupení do systému";
@ -85,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Tmavý";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Výchozí";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -175,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Hero";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Zvýrazněný text";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Klávesová zkratka";
@ -247,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Žádná";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Normální text";
/* Combat rank. [stats.py] */
"Novice" = "Novice";
@ -409,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Systém";
/* Appearance setting. [prefs.py] */
"Theme" = "Schéma";
/* Ranking. [stats.py] */
"Trade" = "Trade";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Total planlos";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Immer im Vordergrund";
/* CQC rank. [stats.py] */
@ -19,9 +19,15 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API-Schlüssel";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Aussehen";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Automatisch Logbucheintrag bei Systemeintritt anlegen";
/* [prefs.py] */
"Automatically open uncharted systems EDSM pages" = "Automatisch die EDSM Seite unerforschter Systeme öffnen";
/* Cmdr stats. [stats.py] */
"Balance" = "Kontostand";
@ -61,6 +67,9 @@
/* Combat rank. [stats.py] */
"Competent" = "Kompetent";
/* Output settings. [prefs.py] */
"Connected to {EDPROXY} at {ADDR}" = "Verbunden mit {EDPROXY} bei {ADDR}";
/* Update button in main window. [EDMarketConnector.py] */
"cooldown {SS}s" = "Wartezeit {SS}s";
@ -79,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Gefährlich";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Dunkel";
/* Combat rank. [stats.py] */
"Deadly" = "Tödlich";
/* Trade rank. [stats.py] */
"Dealer" = "Kleinhändler";
/* Appearance theme setting. [prefs.py] */
"Default" = "Standard";
/* Empire rank. [stats.py] */
"Duke" = "Herzog";
@ -130,6 +145,9 @@
/* [companion.py] */
"Error: Server is down" = "Fehler: Server nicht erreichbar";
/* [companion.py] */
"Error: Verification failed" = "Fehler: Verifizierung fehlgeschlagen";
/* Item in the File menu on Windows. [EDMarketConnector.py] */
"Exit" = "Beenden";
@ -166,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Held";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Hervorgehobener Text";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Hotkey";
@ -238,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Keine Zuweisung";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Standardmäßiger Text";
/* Combat rank. [stats.py] */
"Novice" = "Neuling";
@ -400,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "System";
/* Appearance setting. [prefs.py] */
"Theme" = "Theme";
/* Ranking. [stats.py] */
"Trade" = "Handelsrang";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Always on top";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Appearance";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Automatically make a log entry on entering a system";
@ -85,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Dark";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Default";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -175,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Hero";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Highlighted text";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Hotkey";
@ -247,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "None";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Normal text";
/* Combat rank. [stats.py] */
"Novice" = "Novice";
@ -409,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "System";
/* Appearance setting. [prefs.py] */
"Theme" = "Theme";
/* Ranking. [stats.py] */
"Trade" = "Trade";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Perdido";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Siempre visible";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Apariencia";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Crear automáticamente una entrada en el registro al entrar en un sistema";
@ -64,6 +67,9 @@
/* Combat rank. [stats.py] */
"Competent" = "Competente";
/* Output settings. [prefs.py] */
"Connected to {EDPROXY} at {ADDR}" = "Conectado a {EDPROXY} en {ADDR}";
/* Update button in main window. [EDMarketConnector.py] */
"cooldown {SS}s" = "Espere {SS}s";
@ -82,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Peligroso";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Oscuro";
/* Combat rank. [stats.py] */
"Deadly" = "Letal";
/* Trade rank. [stats.py] */
"Dealer" = "Distribuidor";
/* Appearance theme setting. [prefs.py] */
"Default" = "Por defecto";
/* Empire rank. [stats.py] */
"Duke" = "Duque";
@ -172,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Héroe";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Texto resaltado";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Tecla de acceso directo";
@ -244,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Ninguna";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Texto normal";
/* Combat rank. [stats.py] */
"Novice" = "Novato";
@ -406,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Sistema";
/* Appearance setting. [prefs.py] */
"Theme" = "Tema";
/* Ranking. [stats.py] */
"Trade" = "Comercio";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Vagabond";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Toujours visible";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Apparence";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Ajouter automatiquement une entrée au journal en entrant dans un système";
@ -82,12 +85,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Vétéran";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Foncé";
/* Combat rank. [stats.py] */
"Deadly" = "Létal";
/* Trade rank. [stats.py] */
"Dealer" = "Revendeur";
/* Appearance theme setting. [prefs.py] */
"Default" = "Par défaut";
/* Empire rank. [stats.py] */
"Duke" = "Archiduc";
@ -403,6 +412,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Système";
/* Appearance setting. [prefs.py] */
"Theme" = "Thème";
/* Ranking. [stats.py] */
"Trade" = "Commerçant";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Sempre in primo piano";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Aspetto";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Inserisce automaticamente una log entry entrando in un sistema";
@ -82,12 +85,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Scuro";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Predefinito";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -403,6 +412,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Sistema";
/* Appearance setting. [prefs.py] */
"Theme" = "Tema";
/* Ranking. [stats.py] */
"Trade" = "Trade";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Vienmēr virspusē";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API atslēga";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Izskats";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Automātiski veikt ierakstu ierodoties sistēmā";
@ -82,12 +85,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Tumša";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Noklusējuma";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -403,6 +412,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Sistēma";
/* Appearance setting. [prefs.py] */
"Theme" = "Dizains";
/* Ranking. [stats.py] */
"Trade" = "Trade";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Altijd op voorgrond";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Vormgeving";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Automatisch een log regel aanmaken bij het binnengaan van een stelsel";
@ -85,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Donker";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Standaard";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -409,6 +418,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Systeem";
/* Appearance setting. [prefs.py] */
"Theme" = "Thema";
/* Ranking. [stats.py] */
"Trade" = "Handels rang";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Zawsze na wierzchu";
/* CQC rank. [stats.py] */
@ -19,9 +19,15 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Key";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Wygląd";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Stwórz automatycznie wpis w logu po wejściu do systemu";
/* [prefs.py] */
"Automatically open uncharted systems EDSM pages" = "Automatycznie otwieraj nie skatalogowane systemy w EDSM";
/* Cmdr stats. [stats.py] */
"Balance" = "Saldo";
@ -61,6 +67,9 @@
/* Combat rank. [stats.py] */
"Competent" = "Competent";
/* Output settings. [prefs.py] */
"Connected to {EDPROXY} at {ADDR}" = "Połąćzono do {EDPROXY} na {ADDR}";
/* Update button in main window. [EDMarketConnector.py] */
"cooldown {SS}s" = "Oczekiwanie {SS} sek.";
@ -79,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Ciemny";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Domyślne";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -130,6 +145,9 @@
/* [companion.py] */
"Error: Server is down" = "Błąd: Serwer padł";
/* [companion.py] */
"Error: Verification failed" = "Błąd: Weryfikacja niepoprawna";
/* Item in the File menu on Windows. [EDMarketConnector.py] */
"Exit" = "Zakończ";
@ -166,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Hero";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Podświetlony tekst";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Skr. Klaw.";
@ -238,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Brak";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Zwykły tekst";
/* Combat rank. [stats.py] */
"Novice" = "Novice";
@ -400,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "System";
/* Appearance setting. [prefs.py] */
"Theme" = "Schemat kolorystyczny";
/* Ranking. [stats.py] */
"Trade" = "Trade";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Бесцельный";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Поверх остальных окон";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "Ключ API";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Внешний вид";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Автоматически заносить в журнал полета каждую посещённую систему";
@ -82,12 +85,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Опасный";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Темный";
/* Combat rank. [stats.py] */
"Deadly" = "Смертоносный";
/* Trade rank. [stats.py] */
"Dealer" = "Агент";
/* Appearance theme setting. [prefs.py] */
"Default" = "По умолчанию";
/* Empire rank. [stats.py] */
"Duke" = "Герцог";
@ -403,6 +412,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Система";
/* Appearance setting. [prefs.py] */
"Theme" = "Тема";
/* Ranking. [stats.py] */
"Trade" = "Торговый";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Aimless";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Vedno na vrhu";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API ključ";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Videz";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Shrani podatke v dnevnik leta, ko prispeš v sistem";
@ -61,6 +64,9 @@
/* Combat rank. [stats.py] */
"Competent" = "Competent";
/* Output settings. [prefs.py] */
"Connected to {EDPROXY} at {ADDR}" = "Connected to {EDPROXY} at {ADDR}";
/* Update button in main window. [EDMarketConnector.py] */
"cooldown {SS}s" = "Posodobitev v {SS}s";
@ -79,12 +85,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Dangerous";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Temno";
/* Combat rank. [stats.py] */
"Deadly" = "Deadly";
/* Trade rank. [stats.py] */
"Dealer" = "Dealer";
/* Appearance theme setting. [prefs.py] */
"Default" = "Privzeta";
/* Empire rank. [stats.py] */
"Duke" = "Duke";
@ -400,6 +412,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Sistem";
/* Appearance setting. [prefs.py] */
"Theme" = "Tema";
/* Ranking. [stats.py] */
"Trade" = "Trgovanje";

View File

@ -10,7 +10,7 @@
/* Explorer rank. [stats.py] */
"Aimless" = "Безцільний";
/* System menu entry on Windows. [EDMarketConnector.py] */
/* Appearance setting. [EDMarketConnector.py] */
"Always on top" = "Поверх інших вікон";
/* CQC rank. [stats.py] */
@ -19,6 +19,9 @@
/* EDSM setting. [prefs.py] */
"API Key" = "API Ключ";
/* Tab heading in settings. [prefs.py] */
"Appearance" = "Зовнішній вигляд";
/* Output setting. [prefs.py] */
"Automatically make a log entry on entering a system" = "Автоматично робити запис у журналі на вході в систему";
@ -85,12 +88,18 @@
/* Combat rank. [stats.py] */
"Dangerous" = "Небезпечний";
/* Appearance theme setting. [prefs.py] */
"Dark" = "Темний";
/* Combat rank. [stats.py] */
"Deadly" = "Убивчий";
/* Trade rank. [stats.py] */
"Dealer" = "Дилер";
/* Appearance theme setting. [prefs.py] */
"Default" = "Стандарт";
/* Empire rank. [stats.py] */
"Duke" = "Герцог";
@ -175,6 +184,9 @@
/* CQC rank. [stats.py] */
"Hero" = "Герой";
/* Dark theme color setting. [prefs.py] */
"Highlighted text" = "Виділений текст";
/* Tab heading in settings on Windows. [prefs.py] */
"Hotkey" = "Горяча клавіша";
@ -247,6 +259,9 @@
/* No hotkey/shortcut currently defined. [prefs.py] */
"None" = "Нічого";
/* Dark theme color setting. [prefs.py] */
"Normal text" = "Нормальний текст";
/* Combat rank. [stats.py] */
"Novice" = "Новачок";
@ -409,6 +424,9 @@
/* Main window. [EDMarketConnector.py] */
"System" = "Система";
/* Appearance setting. [prefs.py] */
"Theme" = "Тема";
/* Ranking. [stats.py] */
"Trade" = "Торговий";

View File

@ -18,6 +18,8 @@ Click on the station name to go to its [Elite: Dangerous Database](http://eddb.i
![Windows screenshot](img/win.png) &nbsp; ![Mac screenshot](img/mac.png)
![Windows screenshot](img/win_dark.png) &nbsp; ![Mac screenshot](img/mac_dark.png)
Installation
--------

BIN
img/mac_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
img/win_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -6,7 +6,7 @@ from sys import platform
import Tkinter as tk
import ttk
import tkFileDialog
import tkColorChooser
from ttkHyperlinkLabel import HyperlinkLabel
import myNotebook as nb
@ -14,6 +14,7 @@ from config import applongname, config
from edproxy import edproxy
from hotkey import hotkeymgr
from monitor import monitor
from theme import theme
import plug
@ -62,12 +63,12 @@ class PreferencesDialog(tk.Toplevel):
self.geometry("+%d+%d" % (parent.winfo_rootx(), parent.winfo_rooty()))
# remove decoration
self.resizable(tk.FALSE, tk.FALSE)
if platform=='win32':
self.attributes('-toolwindow', tk.TRUE)
elif platform=='darwin':
# http://wiki.tcl.tk/13428
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')
self.resizable(tk.FALSE, tk.FALSE)
style = ttk.Style()
@ -217,6 +218,33 @@ class PreferencesDialog(tk.Toplevel):
_('Keyboard shortcut') or # Tab heading in settings on OSX
_('Hotkey')) # Tab heading in settings on Windows
self.always_ontop = tk.BooleanVar(value = config.getint('always_ontop'))
self.theme = tk.IntVar(value = config.getint('theme') and 1 or 0)
self.theme_colors = [config.get('dark_text'), config.get('dark_highlight')]
self.theme_prompts = [
_('Normal text'), # Dark theme color setting
_('Highlighted text'), # Dark theme color setting
]
themeframe = nb.Frame(notebook)
themeframe.columnconfigure(2, weight=1)
nb.Label(themeframe).grid(sticky=tk.W) # big spacer
self.ontop_button = nb.Checkbutton(themeframe, text=_('Always on top'), variable=self.always_ontop, command=self.themevarchanged)
self.ontop_button.grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance setting
ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW)
nb.Label(themeframe, text=_('Theme')).grid(columnspan=3, padx=PADX, sticky=tk.W) # Appearance setting
nb.Radiobutton(themeframe, text=_('Default'), variable=self.theme, value=0, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme setting
nb.Radiobutton(themeframe, text=_('Dark'), variable=self.theme, value=1, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme setting
self.theme_label_0 = nb.Label(themeframe, text=self.theme_prompts[0])
self.theme_label_0.grid(row=10, padx=PADX, sticky=tk.W)
self.theme_button_0 = nb.ColoredButton(themeframe, text=_('Station'), background='black', command=lambda:self.themecolorbrowse(0)) # Main window
self.theme_button_0.grid(row=10, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW)
self.theme_label_1 = nb.Label(themeframe, text=self.theme_prompts[1])
self.theme_label_1.grid(row=11, padx=PADX, sticky=tk.W)
self.theme_button_1 = nb.ColoredButton(themeframe, text=' Hutton Orbital ', background='black', command=lambda:self.themecolorbrowse(1)) # Do not translate
self.theme_button_1.grid(row=11, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW)
notebook.add(themeframe, text=_('Appearance')) # Tab heading in settings
# build plugin prefs tabs
for plugname in plug.PLUGINS:
plugframe = plug.get_plugin_pref(plugname, notebook)
@ -237,14 +265,15 @@ class PreferencesDialog(tk.Toplevel):
# Selectively disable buttons depending on output settings
self.proxypoll()
self.themevarchanged()
# disable hotkey for the duration
hotkeymgr.unregister()
# wait for window to appear on screen before calling grab_set
self.parent.wm_attributes('-topmost', 0) # needed for dialog to appear ontop of parent on OSX & Linux
self.wait_visibility()
self.grab_set()
#self.wait_window(self) # causes duplicate events on OSX
def proxypoll(self):
@ -296,6 +325,7 @@ class PreferencesDialog(tk.Toplevel):
def outbrowse(self):
if platform != 'win32':
import tkFileDialog
d = tkFileDialog.askdirectory(parent=self, initialdir=expanduser(self.outdir.get()), title=_('File location'), mustexist=tk.TRUE)
else:
def browsecallback(hwnd, uMsg, lParam, lpData):
@ -328,6 +358,25 @@ class PreferencesDialog(tk.Toplevel):
self.outdir.insert(0, d)
self.outdir['state'] = 'readonly'
def themecolorbrowse(self, index):
(rgb, color) = tkColorChooser.askcolor(self.theme_colors[index], title=self.theme_prompts[index], parent=self.parent)
if color:
self.theme_colors[index] = color
self.themevarchanged()
def themevarchanged(self):
self.theme_button_0['foreground'], self.theme_button_1['foreground'] = self.theme_colors
state = self.theme.get() and tk.NORMAL or tk.DISABLED
self.theme_label_0['state'] = state
self.theme_label_1['state'] = state
self.theme_button_0['state'] = state
self.theme_button_1['state'] = state
if platform == 'linux2':
# Unmanaged windows are always on top on X
self.ontop_button['state'] = self.theme.get() and tk.DISABLED or tk.NORMAL
def hotkeystart(self, event):
event.widget.bind('<KeyPress>', self.hotkeylisten)
event.widget.bind('<KeyRelease>', self.hotkeylisten)
@ -396,6 +445,12 @@ class PreferencesDialog(tk.Toplevel):
config.set('hotkey_always', int(not self.hotkey_only.get()))
config.set('hotkey_mute', int(not self.hotkey_play.get()))
config.set('always_ontop', self.always_ontop.get())
config.set('theme', self.theme.get())
config.set('dark_text', self.theme_colors[0])
config.set('dark_highlight', self.theme_colors[1])
theme.apply(self.parent)
config.set('anonymous', self.out_anon.get())
self._destroy()
@ -412,6 +467,7 @@ class PreferencesDialog(tk.Toplevel):
else:
monitor.stop()
edproxy.stop()
self.parent.wm_attributes('-topmost', config.getint('always_ontop') and 1 or 0)
self.destroy()
if platform == 'darwin':

221
theme.py Normal file
View File

@ -0,0 +1,221 @@
#
# 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_highlight = 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_highlight(self, widget):
assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget
if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame):
self.register_highlight(widget.winfo_children())
self.widgets_highlight.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' : 'black',
'foreground' : config.get('dark_text'),
'activebackground' : config.get('dark_text'),
'activeforeground' : 'black',
'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 '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'])
for widget in self.widgets_highlight:
widget.configure(foreground = self.current['highlight'],
background = self.current['background'])
for pair, gridopts in self.widgets_pair:
(default, dark) = pair
if isinstance(default, tk.Menu):
if theme:
root['menu'] = ''
dark.grid(**gridopts)
else:
root['menu'] = default
dark.grid_remove()
else:
old = theme and default or dark
current = theme and dark or default
old.grid_remove()
current.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, NSColor
root.update_idletasks() # need main window to be created
appearance = NSAppearance.appearanceNamed_(theme and
'NSAppearanceNameVibrantDark' or
'NSAppearanceNameAqua')
for window in NSApplication.sharedApplication().windows():
window.setAppearance_(appearance)
if not self.minwidth:
self.minwidth = root.winfo_width() # Minimum width = width on first creation
# resizable(0,0) doesn't do anything on OSX
root.minsize(self.minwidth, root.winfo_height())
root.maxsize(-1, root.winfo_height())
elif platform == 'win32':
# tk8.5.9/win/tkWinWm.c:342
import ctypes
GWL_STYLE = -16
WS_BORDER = 0x00800000
WS_OVERLAPPEDWINDOW =0x00CF0000
GWL_EXSTYLE = -20
WS_EX_WINDOWEDGE = 0x00000100
WS_EX_APPWINDOW = 0x00040000
root.overrideredirect(theme and 1 or 0) # Destroys any top-level window
root.update_idletasks() # Size and windows styles get recalculated here
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
ctypes.windll.user32.SetWindowLongW(hwnd, GWL_STYLE, theme and WS_BORDER or WS_OVERLAPPEDWINDOW)
ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, theme and WS_EX_APPWINDOW or WS_EX_WINDOWEDGE)
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)
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()