V1.2
@@ -0,0 +1,467 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import configparser
|
||||
from typing import Self
|
||||
from threading import Thread
|
||||
import threading
|
||||
from datetime import datetime
|
||||
import time
|
||||
import socket
|
||||
import os
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
import platform
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
class App(ttk.Frame):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.parent = parent
|
||||
self.parent.title("Tally Triggers V1.2 HTTP")
|
||||
|
||||
|
||||
# Create tabs
|
||||
self.tabControl = ttk.Notebook(self.parent)
|
||||
self.tab1 = ttk.Frame(self.tabControl)
|
||||
self.tab2 = ttk.Frame(self.tabControl)
|
||||
|
||||
|
||||
# Add tabs to the tab control
|
||||
self.tabControl.add(self.tab1, text="Main")
|
||||
self.tabControl.add(self.tab2, text="Settings")
|
||||
self.tabControl.pack(expand=1, fill="both")
|
||||
|
||||
#Load Config
|
||||
self.load_config()
|
||||
|
||||
# Start TCP1 listener thread
|
||||
self.tcp_thread1 = threading.Thread(target=self.tcp_listener, args=(self.TCP_Port1,), daemon=True)
|
||||
self.tcp_thread1.start()
|
||||
|
||||
# Start TCP2 listener thread
|
||||
self.tcp_thread2 = threading.Thread(target=self.tcp_listener, args=(self.TCP_Port2,), daemon=True)
|
||||
self.tcp_thread2.start()
|
||||
|
||||
|
||||
# Create widgets
|
||||
self.setup_widgets()
|
||||
|
||||
|
||||
# Start Tally Box connectivity check thread
|
||||
self.connection_thread = threading.Thread(target=self.check_tally_connection, daemon=True)
|
||||
self.connection_thread.start()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def load_config(self):
|
||||
# Load configuration
|
||||
config = configparser.ConfigParser()
|
||||
config.read('Config.ini')
|
||||
|
||||
# Courts
|
||||
self.Relay_1_Name = config['Courts']['Relay1']
|
||||
self.Relay_2_Name = config['Courts']['Relay2']
|
||||
|
||||
|
||||
#TMCC IPs
|
||||
self.Relay1_TMCCIP = config['TMMC']['Relay1']
|
||||
self.Relay2_TMCCIP = config['TMMC']['Relay2']
|
||||
|
||||
|
||||
#TCP Commands
|
||||
self.Relay1_Command = config['TCP Commands']['Relay1']
|
||||
self.Relay2_Command = config['TCP Commands']['Relay2']
|
||||
|
||||
|
||||
#General
|
||||
self.TallyBoxIP = config['General']['TallyBoxIP']
|
||||
self.TCP_Port1 = int(config['General']['TCP_Port1'])
|
||||
self.TCP_Port2 = int(config['General']['TCP_Port2'])
|
||||
self.On_Time = int(config['General']['On_Time'])
|
||||
self.PosDistanceThreshold = int(config['General']['Pos_Dist'])
|
||||
self.NegDistanceThreshold = int(config['General']['Neg_Dist'])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def setup_widgets(self):
|
||||
|
||||
|
||||
#####################################################################################
|
||||
# Relay 1 Frame
|
||||
self.Relay1_frame = ttk.LabelFrame(self.tab1, text="Relay 1", padding=(20, 10))
|
||||
self.Relay1_frame.grid(
|
||||
row=0, column=0, padx=(5, 5), pady=(20, 10), sticky="nsew"
|
||||
)
|
||||
|
||||
# Relay 1
|
||||
self.Relay1 = ttk.Label(self.Relay1_frame,text=self.Relay_1_Name,justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay1.grid(row=0, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
|
||||
# Canvas for the circle
|
||||
self.Relay1Status = tk.Canvas(self.Relay1_frame, width=45, height=50)
|
||||
self.Relay1Status.grid(row=1, column=0, pady=(0, 10))
|
||||
|
||||
# Draw circle
|
||||
self.oval_id1 = self.Relay1Status.create_oval(10, 10, 40, 40, fill="red", outline="grey")
|
||||
|
||||
# Relay 1 TMCC Connection
|
||||
self.Relay1TMCCConnection = ttk.Label(self.Relay1_frame,text="TMCC",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay1TMCCConnection.grid(row=2, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Relay 1 last trigger time
|
||||
self.Relay1time = ttk.Label(self.Relay1_frame,text="Never",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay1time.grid(row=3, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Test Button
|
||||
self.Relay1button = ttk.Button(self.Relay1_frame, text="Test", style="Accent.TButton",width = 12, command=self.publish_relay1)
|
||||
self.Relay1button.grid(row=4, column=0, padx=0, pady=10, sticky="nsew")
|
||||
|
||||
#####################################################################################
|
||||
# Relay 2 Frame
|
||||
self.Relay2_frame = ttk.LabelFrame(self.tab1, text="Relay 2", padding=(20, 10))
|
||||
self.Relay2_frame.grid(
|
||||
row=0, column=1, padx=(5, 5), pady=(20, 10), sticky="nsew"
|
||||
)
|
||||
|
||||
# Entry Relay 2
|
||||
self.Relay2 = ttk.Label(self.Relay2_frame,text=self.Relay_2_Name,justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay2.grid(row=0, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Canvas for the circle
|
||||
self.Relay2Status = tk.Canvas(self.Relay2_frame, width=45, height=50)
|
||||
self.Relay2Status.grid(row=1, column=0, pady=(0, 10))
|
||||
|
||||
# Draw circle
|
||||
self.oval_id2 = self.Relay2Status.create_oval(10, 10, 40, 40, fill="red", outline="grey")
|
||||
|
||||
# Relay 2 TMCC Connection
|
||||
self.Relay2TMCCConnection = ttk.Label(self.Relay2_frame,text="TMCC",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay2TMCCConnection.grid(row=2, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Entry Relay 2 last trigger time
|
||||
self.Relay2time = ttk.Label(self.Relay2_frame,text="Never",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay2time.grid(row=3, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Test Button
|
||||
self.Relay2button = ttk.Button(self.Relay2_frame, text="Test", style="Accent.TButton",width = 12, command=self.publish_relay2)
|
||||
self.Relay2button.grid(row=4, column=0, padx=0, pady=10, sticky="nsew")
|
||||
|
||||
|
||||
#####################################################################################
|
||||
# Extras Frame
|
||||
self.Extras_frame = ttk.LabelFrame(self.tab1, text="Extras", padding=(20, 10))
|
||||
self.Extras_frame.grid(
|
||||
row=1, column=0, columnspan = 8, padx=(5, 5), pady=(5, 5), sticky="nsew"
|
||||
)
|
||||
|
||||
# Connection Status
|
||||
self.ConnectionStatus = ttk.Label(self.Extras_frame,text="Connected to Tally Box:",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.ConnectionStatus.grid(row=0, column=0, padx=0, pady=(0, 0), sticky="ew")
|
||||
|
||||
# Canvas for the circle
|
||||
self.ConnectionStatusLight = tk.Canvas(self.Extras_frame, width=50, height=50)
|
||||
self.ConnectionStatusLight.grid(row=0, column=1, pady=(0, 0))
|
||||
|
||||
# Draw circle
|
||||
self.oval_id9 = self.ConnectionStatusLight.create_oval(10, 10, 40, 40, fill="red", outline="grey")
|
||||
|
||||
# Test All Button
|
||||
self.TestAllButton = ttk.Button(self.Extras_frame, text="Test All", style="Accent.TButton",width = 12, command=self.publish_relay_all)
|
||||
self.TestAllButton.grid(row=0, column=4, padx=(50,0), pady=0, sticky="nsew")
|
||||
|
||||
|
||||
#######################################################################################
|
||||
#####################################################################################
|
||||
|
||||
|
||||
|
||||
# Relay 1 Settings Frame
|
||||
self.Relay1_frame = ttk.LabelFrame(self.tab2, text="Relay 1", padding=(20, 10))
|
||||
self.Relay1_frame.grid(
|
||||
row=0, column=0, padx=(5, 5), pady=(20, 10), sticky="nsew"
|
||||
)
|
||||
|
||||
# Label Relay 1 Courtname
|
||||
self.Relay1Name = ttk.Label(self.Relay1_frame,text="Court Name",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay1Name.grid(row=0, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Entry Relay 1 Courtname
|
||||
self.Relay1Entry = ttk.Entry(self.Relay1_frame, textvariable=self.Relay_1_Name, justify="center", font=("-size", 10, "-weight", "bold"))
|
||||
self.Relay1Entry.insert(0, self.Relay_1_Name)
|
||||
self.Relay1Entry.grid(row=1, column=0, padx=0, pady=(0, 20), sticky="ew")
|
||||
|
||||
# Save Button
|
||||
self.Save1button = ttk.Button(self.Relay1_frame, text="Save", style="Accent.TButton",width = 12, command=self.save_relay_names)
|
||||
self.Save1button.grid(row=2, column=0, padx=0, pady=10, sticky="nsew")
|
||||
###########################################################################################################################################
|
||||
# Relay 2 Settings Frame
|
||||
self.Relay2_frame = ttk.LabelFrame(self.tab2, text="Relay 2", padding=(20, 10))
|
||||
self.Relay2_frame.grid(
|
||||
row=0, column=1, padx=(5, 5), pady=(20, 10), sticky="nsew"
|
||||
)
|
||||
|
||||
# Label Relay 2 Courtname
|
||||
self.Relay2Name = ttk.Label(self.Relay2_frame,text="Court Name",justify="center",font=("-size", 10, "-weight", "bold"),
|
||||
)
|
||||
self.Relay2Name.grid(row=0, column=0, padx=0, pady=(0, 20), sticky="ns")
|
||||
|
||||
# Entry Relay 2 Courtname
|
||||
self.Relay2Entry = ttk.Entry(self.Relay2_frame, textvariable=self.Relay_2_Name, justify="center", font=("-size", 10, "-weight", "bold"))
|
||||
self.Relay2Entry.insert(0, self.Relay_2_Name)
|
||||
self.Relay2Entry.grid(row=1, column=0, padx=0, pady=(0, 20), sticky="ew")
|
||||
|
||||
# Save Button
|
||||
self.Save2button = ttk.Button(self.Relay2_frame, text="Save", style="Accent.TButton",width = 12, command=self.save_relay_names)
|
||||
self.Save2button.grid(row=2, column=0, padx=0, pady=10, sticky="nsew")
|
||||
###########################################################################################################################################
|
||||
|
||||
# Function to send a TCP message to the TALLY BOX
|
||||
def send_message(self, command, timeout=5):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.settimeout(timeout)
|
||||
s.connect((self.TallyBoxIP, 17494))
|
||||
|
||||
# command may already be bytes, so handle both cases
|
||||
if isinstance(command, str):
|
||||
s.sendall(bytes.fromhex(command))
|
||||
else:
|
||||
s.sendall(command)
|
||||
|
||||
data = s.recv(1024)
|
||||
return data # Return response bytes
|
||||
except (socket.timeout, ConnectionRefusedError, socket.error) as e:
|
||||
print(f"Error sending message: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def publish_relay1(self):
|
||||
def toggle_relay1():
|
||||
relay_id = 1
|
||||
|
||||
# ---- Turn ON ----
|
||||
message = bytes([0x20, relay_id, 0x00])
|
||||
response = self.send_message(message)
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
print(f"[{current_time}] Sent command: 20 {relay_id:02X} 00 - Relay {relay_id} ON")
|
||||
|
||||
if response:
|
||||
resp_val = response[0]
|
||||
print(f"[{current_time}] Device response: {resp_val}")
|
||||
if resp_val == 0:
|
||||
self.Relay1Status.itemconfig(self.oval_id1, fill="green")
|
||||
self.Relay1time.config(text=current_time)
|
||||
else:
|
||||
print(f"[{current_time}] Relay {relay_id} ON failed (response={resp_val})")
|
||||
return
|
||||
else:
|
||||
print(f"[{current_time}] No response from device for Relay {relay_id} ON")
|
||||
return
|
||||
|
||||
time.sleep(self.On_Time)
|
||||
|
||||
# ---- Turn OFF ----
|
||||
message = bytes([0x21, relay_id, 0x00])
|
||||
response = self.send_message(message)
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
print(f"[{current_time}] Sent command: 21 {relay_id:02X} 00 - Relay {relay_id} OFF")
|
||||
|
||||
if response:
|
||||
resp_val = response[0]
|
||||
print(f"[{current_time}] Device response: {resp_val}")
|
||||
if resp_val == 0:
|
||||
self.Relay1Status.itemconfig(self.oval_id2, fill="red")
|
||||
else:
|
||||
print(f"[{current_time}] Relay {relay_id} OFF failed (response={resp_val})")
|
||||
else:
|
||||
print(f"[{current_time}] No response from device for Relay {relay_id} OFF")
|
||||
|
||||
threading.Thread(target=toggle_relay1, daemon=True).start()
|
||||
|
||||
|
||||
def publish_relay2(self):
|
||||
def toggle_relay2():
|
||||
relay_id = 2
|
||||
|
||||
# ---- Turn ON ----
|
||||
message = bytes([0x20, relay_id, 0x00])
|
||||
response = self.send_message(message)
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
print(f"[{current_time}] Sent command: 20 {relay_id:02X} 00 - Relay {relay_id} ON")
|
||||
|
||||
if response:
|
||||
resp_val = response[0]
|
||||
print(f"[{current_time}] Device response: {resp_val}")
|
||||
if resp_val == 0:
|
||||
self.Relay2Status.itemconfig(self.oval_id2, fill="green")
|
||||
self.Relay2time.config(text=current_time)
|
||||
else:
|
||||
print(f"[{current_time}] Relay {relay_id} ON failed (response={resp_val})")
|
||||
return
|
||||
else:
|
||||
print(f"[{current_time}] No response from device for Relay {relay_id} ON")
|
||||
return
|
||||
|
||||
time.sleep(self.On_Time)
|
||||
|
||||
# ---- Turn OFF ----
|
||||
message = bytes([0x21, relay_id, 0x00])
|
||||
response = self.send_message(message)
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
print(f"[{current_time}] Sent command: 21 {relay_id:02X} 00 - Relay {relay_id} OFF")
|
||||
|
||||
if response:
|
||||
resp_val = response[0]
|
||||
print(f"[{current_time}] Device response: {resp_val}")
|
||||
if resp_val == 0:
|
||||
self.Relay2Status.itemconfig(self.oval_id2, fill="red")
|
||||
else:
|
||||
print(f"[{current_time}] Relay {relay_id} OFF failed (response={resp_val})")
|
||||
else:
|
||||
print(f"[{current_time}] No response from device for Relay {relay_id} OFF")
|
||||
|
||||
threading.Thread(target=toggle_relay2, daemon=True).start()
|
||||
|
||||
|
||||
|
||||
def publish_relay_all(self):
|
||||
self.publish_relay1()
|
||||
self.publish_relay2()
|
||||
|
||||
def check_tally_connection(self):
|
||||
"""Continuously checks if the Tally Box is reachable and updates the status light."""
|
||||
while True:
|
||||
if self.ping_tally_box():
|
||||
self.ConnectionStatusLight.itemconfig(self.oval_id9, fill="green") # Change to green if reachable
|
||||
else:
|
||||
self.ConnectionStatusLight.itemconfig(self.oval_id9, fill="red") # Change to red if unreachable
|
||||
time.sleep(5) # Check every 5 seconds
|
||||
|
||||
def ping_tally_box(self):
|
||||
"""Pings the Tally Box IP to check connectivity."""
|
||||
param = "-n" if platform.system().lower() == "windows" else "-c"
|
||||
command = ["ping", param, "1", self.TallyBoxIP]
|
||||
try:
|
||||
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||||
return True # Ping was successful
|
||||
except subprocess.CalledProcessError:
|
||||
return False # Ping failed
|
||||
|
||||
def save_relay_names(self):
|
||||
"""Saves the Relay 1 and Relay 2 names to the Config.ini file and updates the UI labels."""
|
||||
new_relay1_name = self.Relay1Entry.get()
|
||||
new_relay2_name = self.Relay2Entry.get()
|
||||
|
||||
# Update labels in the UI
|
||||
self.Relay1.config(text=new_relay1_name)
|
||||
self.Relay2.config(text=new_relay2_name)
|
||||
|
||||
# Update the config file
|
||||
config = configparser.ConfigParser()
|
||||
config.read('Config.ini')
|
||||
|
||||
if 'Courts' not in config:
|
||||
config['Courts'] = {}
|
||||
|
||||
config['Courts']['Relay1'] = new_relay1_name
|
||||
config['Courts']['Relay2'] = new_relay2_name
|
||||
|
||||
with open('Config.ini', 'w') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
print("Config updated successfully!")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def tcp_listener(self, port):
|
||||
# TCP/IP socket setup
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_socket.bind(('0.0.0.0', port)) # Bind to all interfaces
|
||||
server_socket.listen(1) # Listen for incoming connections
|
||||
|
||||
print(f"TCP server listening on port {port} \n")
|
||||
|
||||
while True:
|
||||
# Accept a connection
|
||||
conn, addr = server_socket.accept()
|
||||
print(f"Connection from {addr} on port {port}")
|
||||
|
||||
try:
|
||||
# Receive the message
|
||||
message = conn.recv(1024).decode('utf-8').strip()
|
||||
print(f"Received raw message: {message}")
|
||||
|
||||
# Extract values using regular expressions
|
||||
close_call_match = re.search(r'"Close Call":\s*"(\w+)"', message)
|
||||
distance_match = re.search(r'"Distance":\s*(-?[\d\.]+)', message)
|
||||
|
||||
if not (close_call_match and distance_match):
|
||||
print("Error: Invalid message format")
|
||||
continue # Skip processing this message
|
||||
|
||||
# Convert extracted values
|
||||
close_call = close_call_match.group(1) == "True"
|
||||
distance = float(distance_match.group(1))
|
||||
|
||||
# Check if Close Call is True and within tolerance
|
||||
if close_call:
|
||||
if port == self.TCP_Port1:
|
||||
self.publish_relay1()
|
||||
elif port == self.TCP_Port2:
|
||||
self.publish_relay2()
|
||||
else:
|
||||
print(f"Message received but conditions not met for port {port}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing message: {e}")
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
root.title("Tally Triggers V1.2 HTTP")
|
||||
root.iconbitmap(r'Resources/icon1.ico')
|
||||
|
||||
|
||||
# Simply set the theme
|
||||
root.tk.call("source", "Resources/azure.tcl")
|
||||
root.tk.call("set_theme", "dark")
|
||||
|
||||
app = App(root)
|
||||
app.pack(fill="both", expand=True)
|
||||
|
||||
|
||||
# Set a minsize for the window, and place it in the middle
|
||||
root.update()
|
||||
root.minsize(root.winfo_width(), root.winfo_height())
|
||||
|
||||
|
||||
root.mainloop()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[Courts]
|
||||
relay1 = Stadium 1
|
||||
relay2 = Stadium 2
|
||||
|
||||
[TMMC]
|
||||
relay1 = 10.10.0.51
|
||||
relay2 = 10.10.1.51
|
||||
|
||||
[TCP Commands]
|
||||
relay1 = run replay 1
|
||||
relay2 = run replay 2
|
||||
|
||||
[General]
|
||||
tallyboxip = 127.0.0.1
|
||||
tcp_port1 = 42000
|
||||
tcp_port2 = 42001
|
||||
on_time = 5
|
||||
pos_dist = 10
|
||||
neg_dist = 10
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Copyright © 2021 rdbende <rdbende@gmail.com>
|
||||
|
||||
source [file join [file dirname [info script]] theme light.tcl]
|
||||
source [file join [file dirname [info script]] theme dark.tcl]
|
||||
|
||||
option add *tearOff 0
|
||||
|
||||
proc set_theme {mode} {
|
||||
if {$mode == "dark"} {
|
||||
ttk::style theme use "azure-dark"
|
||||
|
||||
array set colors {
|
||||
-fg "#ffffff"
|
||||
-bg "#333333"
|
||||
-disabledfg "#ffffff"
|
||||
-disabledbg "#737373"
|
||||
-selectfg "#ffffff"
|
||||
-selectbg "#007fff"
|
||||
}
|
||||
|
||||
ttk::style configure . \
|
||||
-background $colors(-bg) \
|
||||
-foreground $colors(-fg) \
|
||||
-troughcolor $colors(-bg) \
|
||||
-focuscolor $colors(-selectbg) \
|
||||
-selectbackground $colors(-selectbg) \
|
||||
-selectforeground $colors(-selectfg) \
|
||||
-insertcolor $colors(-fg) \
|
||||
-insertwidth 1 \
|
||||
-fieldbackground $colors(-selectbg) \
|
||||
-font {"Segoe Ui" 10} \
|
||||
-borderwidth 1 \
|
||||
-relief flat
|
||||
|
||||
tk_setPalette background [ttk::style lookup . -background] \
|
||||
foreground [ttk::style lookup . -foreground] \
|
||||
highlightColor [ttk::style lookup . -focuscolor] \
|
||||
selectBackground [ttk::style lookup . -selectbackground] \
|
||||
selectForeground [ttk::style lookup . -selectforeground] \
|
||||
activeBackground [ttk::style lookup . -selectbackground] \
|
||||
activeForeground [ttk::style lookup . -selectforeground]
|
||||
|
||||
ttk::style map . -foreground [list disabled $colors(-disabledfg)]
|
||||
|
||||
option add *font [ttk::style lookup . -font]
|
||||
option add *Menu.selectcolor $colors(-fg)
|
||||
|
||||
} elseif {$mode == "light"} {
|
||||
ttk::style theme use "azure-light"
|
||||
|
||||
array set colors {
|
||||
-fg "#000000"
|
||||
-bg "#ffffff"
|
||||
-disabledfg "#737373"
|
||||
-disabledbg "#ffffff"
|
||||
-selectfg "#ffffff"
|
||||
-selectbg "#007fff"
|
||||
}
|
||||
|
||||
ttk::style configure . \
|
||||
-background $colors(-bg) \
|
||||
-foreground $colors(-fg) \
|
||||
-troughcolor $colors(-bg) \
|
||||
-focuscolor $colors(-selectbg) \
|
||||
-selectbackground $colors(-selectbg) \
|
||||
-selectforeground $colors(-selectfg) \
|
||||
-insertcolor $colors(-fg) \
|
||||
-insertwidth 1 \
|
||||
-fieldbackground $colors(-selectbg) \
|
||||
-font {"Segoe Ui" 10} \
|
||||
-borderwidth 1 \
|
||||
-relief flat
|
||||
|
||||
tk_setPalette background [ttk::style lookup . -background] \
|
||||
foreground [ttk::style lookup . -foreground] \
|
||||
highlightColor [ttk::style lookup . -focuscolor] \
|
||||
selectBackground [ttk::style lookup . -selectbackground] \
|
||||
selectForeground [ttk::style lookup . -selectforeground] \
|
||||
activeBackground [ttk::style lookup . -selectbackground] \
|
||||
activeForeground [ttk::style lookup . -selectforeground]
|
||||
|
||||
ttk::style map . -foreground [list disabled $colors(-disabledfg)]
|
||||
|
||||
option add *font [ttk::style lookup . -font]
|
||||
option add *Menu.selectcolor $colors(-fg)
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,537 @@
|
||||
# Copyright (c) 2021 rdbende <rdbende@gmail.com>
|
||||
|
||||
# The Azure theme is a beautiful modern ttk theme inspired by Microsoft's fluent design.
|
||||
|
||||
package require Tk 8.6
|
||||
|
||||
namespace eval ttk::theme::azure-dark {
|
||||
variable version 2.0
|
||||
package provide ttk::theme::azure-dark $version
|
||||
|
||||
ttk::style theme create azure-dark -parent clam -settings {
|
||||
proc load_images {imgdir} {
|
||||
variable I
|
||||
foreach file [glob -directory $imgdir *.png] {
|
||||
set img [file tail [file rootname $file]]
|
||||
set I($img) [image create photo -file $file -format png]
|
||||
}
|
||||
}
|
||||
|
||||
load_images [file join [file dirname [info script]] dark]
|
||||
|
||||
array set colors {
|
||||
-fg "#ffffff"
|
||||
-bg "#333333"
|
||||
-disabledfg "#ffffff"
|
||||
-disabledbg "#737373"
|
||||
-selectfg "#ffffff"
|
||||
-selectbg "#007fff"
|
||||
}
|
||||
|
||||
ttk::style layout TButton {
|
||||
Button.button -children {
|
||||
Button.padding -children {
|
||||
Button.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Toolbutton {
|
||||
Toolbutton.button -children {
|
||||
Toolbutton.padding -children {
|
||||
Toolbutton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TMenubutton {
|
||||
Menubutton.button -children {
|
||||
Menubutton.padding -children {
|
||||
Menubutton.indicator -side right
|
||||
Menubutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TOptionMenu {
|
||||
OptionMenu.button -children {
|
||||
OptionMenu.padding -children {
|
||||
OptionMenu.indicator -side right
|
||||
OptionMenu.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Accent.TButton {
|
||||
AccentButton.button -children {
|
||||
AccentButton.padding -children {
|
||||
AccentButton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TCheckbutton {
|
||||
Checkbutton.button -children {
|
||||
Checkbutton.padding -children {
|
||||
Checkbutton.indicator -side left
|
||||
Checkbutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Switch.TCheckbutton {
|
||||
Switch.button -children {
|
||||
Switch.padding -children {
|
||||
Switch.indicator -side left
|
||||
Switch.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Toggle.TButton {
|
||||
ToggleButton.button -children {
|
||||
ToggleButton.padding -children {
|
||||
ToggleButton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TRadiobutton {
|
||||
Radiobutton.button -children {
|
||||
Radiobutton.padding -children {
|
||||
Radiobutton.indicator -side left
|
||||
Radiobutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.TScrollbar {
|
||||
Vertical.Scrollbar.trough -sticky ns -children {
|
||||
Vertical.Scrollbar.thumb -expand true
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.TScrollbar {
|
||||
Horizontal.Scrollbar.trough -sticky ew -children {
|
||||
Horizontal.Scrollbar.thumb -expand true
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TCombobox {
|
||||
Combobox.field -sticky nswe -children {
|
||||
Combobox.padding -expand true -sticky nswe -children {
|
||||
Combobox.textarea -sticky nswe
|
||||
}
|
||||
}
|
||||
Combobox.button -side right -sticky ns -children {
|
||||
Combobox.arrow -sticky nsew
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TSpinbox {
|
||||
Spinbox.field -sticky nsew -children {
|
||||
Spinbox.padding -expand true -sticky nswe -children {
|
||||
Spinbox.textarea -sticky nswe
|
||||
}
|
||||
|
||||
}
|
||||
Spinbox.button -side right -sticky ns -children {
|
||||
null -side right -children {
|
||||
Spinbox.uparrow -side top
|
||||
Spinbox.downarrow -side bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.TSeparator {
|
||||
Horizontal.separator -sticky nswe
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.TSeparator {
|
||||
Vertical.separator -sticky nswe
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.Tick.TScale {
|
||||
Horizontal.TickScale.trough -sticky ew -children {
|
||||
Horizontal.TickScale.slider -sticky w
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.Tick.TScale {
|
||||
Vertical.TickScale.trough -sticky ns -children {
|
||||
Vertical.TickScale.slider -sticky n
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Card.TFrame {
|
||||
Card.field {
|
||||
Card.padding -expand 1
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TLabelframe {
|
||||
Labelframe.border {
|
||||
Labelframe.padding -expand 1 -children {
|
||||
Labelframe.label -side right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TNotebook.Tab {
|
||||
Notebook.tab -children {
|
||||
Notebook.padding -side top -children {
|
||||
Notebook.label -side top -sticky {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Treeview.Item {
|
||||
Treeitem.padding -sticky nswe -children {
|
||||
Treeitem.indicator -side left -sticky {}
|
||||
Treeitem.image -side left -sticky {}
|
||||
Treeitem.text -side left -sticky {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Elements
|
||||
|
||||
# Button
|
||||
ttk::style configure TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create Button.button image \
|
||||
[list $I(rect-basic) \
|
||||
{selected disabled} $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
selected $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Toolbutton
|
||||
ttk::style configure Toolbutton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create Toolbutton.button image \
|
||||
[list $I(empty) \
|
||||
{selected disabled} $I(empty) \
|
||||
disabled $I(empty) \
|
||||
pressed $I(rect-basic) \
|
||||
selected $I(rect-basic) \
|
||||
active $I(rect-basic) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Menubutton
|
||||
ttk::style configure TMenubutton -padding {8 4 4 4}
|
||||
|
||||
ttk::style element create Menubutton.button \
|
||||
image [list $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
ttk::style element create Menubutton.indicator \
|
||||
image [list $I(down) \
|
||||
active $I(down) \
|
||||
pressed $I(down) \
|
||||
disabled $I(down) \
|
||||
] -width 15 -sticky e
|
||||
|
||||
# OptionMenu
|
||||
ttk::style configure TOptionMenu -padding {8 4 4 4}
|
||||
|
||||
ttk::style element create OptionMenu.button \
|
||||
image [list $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
ttk::style element create OptionMenu.indicator \
|
||||
image [list $I(down) \
|
||||
active $I(down) \
|
||||
pressed $I(down) \
|
||||
disabled $I(down) \
|
||||
] -width 15 -sticky e
|
||||
|
||||
# AccentButton
|
||||
ttk::style configure Accent.TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create AccentButton.button image \
|
||||
[list $I(rect-accent) \
|
||||
{selected disabled} $I(rect-accent-hover) \
|
||||
disabled $I(rect-accent-hover) \
|
||||
pressed $I(rect-accent) \
|
||||
selected $I(rect-accent) \
|
||||
active $I(rect-accent-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Checkbutton
|
||||
ttk::style configure TCheckbutton -padding 4
|
||||
|
||||
ttk::style element create Checkbutton.indicator image \
|
||||
[list $I(box-basic) \
|
||||
{alternate disabled} $I(check-tri-basic) \
|
||||
{selected disabled} $I(check-basic) \
|
||||
disabled $I(box-basic) \
|
||||
{pressed alternate} $I(check-tri-hover) \
|
||||
{active alternate} $I(check-tri-hover) \
|
||||
alternate $I(check-tri-accent) \
|
||||
{pressed selected} $I(check-hover) \
|
||||
{active selected} $I(check-hover) \
|
||||
selected $I(check-accent) \
|
||||
{pressed !selected} $I(rect-hover) \
|
||||
active $I(box-hover) \
|
||||
] -width 26 -sticky w
|
||||
|
||||
# Switch
|
||||
ttk::style element create Switch.indicator image \
|
||||
[list $I(off-basic) \
|
||||
{selected disabled} $I(on-basic) \
|
||||
disabled $I(off-basic) \
|
||||
{pressed selected} $I(on-accent) \
|
||||
{active selected} $I(on-accent) \
|
||||
selected $I(on-accent) \
|
||||
{pressed !selected} $I(off-basic) \
|
||||
active $I(off-basic) \
|
||||
] -width 46 -sticky w
|
||||
|
||||
# ToggleButton
|
||||
ttk::style configure Toggle.TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create ToggleButton.button image \
|
||||
[list $I(rect-basic) \
|
||||
{selected disabled} $I(rect-accent-hover) \
|
||||
disabled $I(rect-basic) \
|
||||
{pressed selected} $I(rect-basic) \
|
||||
{active selected} $I(rect-accent) \
|
||||
selected $I(rect-accent) \
|
||||
{pressed !selected} $I(rect-accent) \
|
||||
active $I(rect-basic) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Radiobutton
|
||||
ttk::style configure TRadiobutton -padding 4
|
||||
|
||||
ttk::style element create Radiobutton.indicator image \
|
||||
[list $I(outline-basic) \
|
||||
{alternate disabled} $I(radio-tri-basic) \
|
||||
{selected disabled} $I(radio-basic) \
|
||||
disabled $I(outline-basic) \
|
||||
{pressed alternate} $I(radio-tri-hover) \
|
||||
{active alternate} $I(radio-tri-hover) \
|
||||
alternate $I(radio-tri-accent) \
|
||||
{pressed selected} $I(radio-hover) \
|
||||
{active selected} $I(radio-hover) \
|
||||
selected $I(radio-accent) \
|
||||
{pressed !selected} $I(circle-hover) \
|
||||
active $I(outline-hover) \
|
||||
] -width 26 -sticky w
|
||||
|
||||
# Scrollbar
|
||||
ttk::style element create Horizontal.Scrollbar.trough image $I(hor-basic) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Horizontal.Scrollbar.thumb \
|
||||
image [list $I(hor-accent) \
|
||||
disabled $I(hor-basic) \
|
||||
pressed $I(hor-hover) \
|
||||
active $I(hor-hover) \
|
||||
] -sticky ew
|
||||
|
||||
ttk::style element create Vertical.Scrollbar.trough image $I(vert-basic) \
|
||||
-sticky ns
|
||||
|
||||
ttk::style element create Vertical.Scrollbar.thumb \
|
||||
image [list $I(vert-accent) \
|
||||
disabled $I(vert-basic) \
|
||||
pressed $I(vert-hover) \
|
||||
active $I(vert-hover) \
|
||||
] -sticky ns
|
||||
|
||||
# Scale
|
||||
ttk::style element create Horizontal.Scale.trough image $I(scale-hor) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Horizontal.Scale.slider \
|
||||
image [list $I(circle-accent) \
|
||||
disabled $I(circle-basic) \
|
||||
pressed $I(circle-hover) \
|
||||
active $I(circle-hover) \
|
||||
] -sticky {}
|
||||
|
||||
ttk::style element create Vertical.Scale.trough image $I(scale-vert) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Vertical.Scale.slider \
|
||||
image [list $I(circle-accent) \
|
||||
disabled $I(circle-basic) \
|
||||
pressed $I(circle-hover) \
|
||||
active $I(circle-hover) \
|
||||
] -sticky {}
|
||||
|
||||
# Tickscale
|
||||
ttk::style element create Horizontal.TickScale.trough image $I(scale-hor) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Horizontal.TickScale.slider \
|
||||
image [list $I(tick-hor-accent) \
|
||||
disabled $I(tick-hor-basic) \
|
||||
pressed $I(tick-hor-hover) \
|
||||
active $I(tick-hor-hover) \
|
||||
] -sticky {}
|
||||
|
||||
ttk::style element create Vertical.TickScale.trough image $I(scale-vert) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Vertical.TickScale.slider \
|
||||
image [list $I(tick-vert-accent) \
|
||||
disabled $I(tick-vert-basic) \
|
||||
pressed $I(tick-vert-hover) \
|
||||
active $I(tick-vert-hover) \
|
||||
] -sticky {}
|
||||
|
||||
# Progressbar
|
||||
ttk::style element create Horizontal.Progressbar.trough image $I(hor-basic) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Horizontal.Progressbar.pbar image $I(hor-accent) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Vertical.Progressbar.trough image $I(vert-basic) \
|
||||
-sticky ns
|
||||
|
||||
ttk::style element create Vertical.Progressbar.pbar image $I(vert-accent) \
|
||||
-sticky ns
|
||||
|
||||
# Entry
|
||||
ttk::style element create Entry.field \
|
||||
image [list $I(box-basic) \
|
||||
{focus hover} $I(box-accent) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8} -sticky news
|
||||
|
||||
# Combobox
|
||||
ttk::style map TCombobox -selectbackground [list \
|
||||
{!focus} $colors(-selectbg) \
|
||||
{readonly hover} $colors(-selectbg) \
|
||||
{readonly focus} $colors(-selectbg) \
|
||||
]
|
||||
|
||||
ttk::style map TCombobox -selectforeground [list \
|
||||
{!focus} $colors(-selectfg) \
|
||||
{readonly hover} $colors(-selectfg) \
|
||||
{readonly focus} $colors(-selectfg) \
|
||||
]
|
||||
|
||||
ttk::style element create Combobox.field \
|
||||
image [list $I(box-basic) \
|
||||
{readonly disabled} $I(rect-basic) \
|
||||
{readonly pressed} $I(rect-basic) \
|
||||
{readonly focus hover} $I(button-hover) \
|
||||
{readonly focus} $I(button-hover) \
|
||||
{readonly hover} $I(button-hover) \
|
||||
{focus hover} $I(box-accent) \
|
||||
readonly $I(rect-basic) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8}
|
||||
|
||||
ttk::style element create Combobox.button \
|
||||
image [list $I(combo-button-basic) \
|
||||
{!readonly focus} $I(combo-button-focus) \
|
||||
{readonly focus} $I(combo-button-hover) \
|
||||
{readonly hover} $I(combo-button-hover)
|
||||
] -border 5 -padding {2 6 6 6}
|
||||
|
||||
ttk::style element create Combobox.arrow image $I(down) \
|
||||
-width 15 -sticky e
|
||||
|
||||
# Spinbox
|
||||
ttk::style element create Spinbox.field \
|
||||
image [list $I(box-basic) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8} -sticky news
|
||||
|
||||
ttk::style element create Spinbox.uparrow \
|
||||
image [list $I(up) \
|
||||
disabled $I(up) \
|
||||
pressed $I(up-accent) \
|
||||
active $I(up-accent) \
|
||||
] -border 4 -width 15 -sticky e
|
||||
|
||||
ttk::style element create Spinbox.downarrow \
|
||||
image [list $I(down) \
|
||||
disabled $I(down) \
|
||||
pressed $I(down-accent) \
|
||||
active $I(down-accent) \
|
||||
] -border 4 -width 15 -sticky e
|
||||
|
||||
ttk::style element create Spinbox.button \
|
||||
image [list $I(combo-button-basic) \
|
||||
{!readonly focus} $I(combo-button-focus) \
|
||||
{readonly focus} $I(combo-button-hover) \
|
||||
{readonly hover} $I(combo-button-hover)
|
||||
] -border 5 -padding {2 6 6 6}
|
||||
|
||||
# Sizegrip
|
||||
ttk::style element create Sizegrip.sizegrip image $I(size) \
|
||||
-sticky ewns
|
||||
|
||||
# Separator
|
||||
ttk::style element create Horizontal.separator image $I(separator)
|
||||
|
||||
ttk::style element create Vertical.separator image $I(separator)
|
||||
|
||||
# Card
|
||||
ttk::style element create Card.field image $I(card) \
|
||||
-border 10 -padding 4 -sticky news
|
||||
|
||||
# Labelframe
|
||||
ttk::style element create Labelframe.border image $I(card) \
|
||||
-border 5 -padding 4 -sticky news
|
||||
|
||||
# Notebook
|
||||
ttk::style element create Notebook.client \
|
||||
image $I(notebook) -border 5
|
||||
|
||||
ttk::style element create Notebook.tab \
|
||||
image [list $I(tab-disabled) \
|
||||
selected $I(tab-basic) \
|
||||
active $I(tab-hover) \
|
||||
] -border 5 -padding {14 4}
|
||||
|
||||
# Treeview
|
||||
ttk::style element create Treeview.field image $I(card) \
|
||||
-border 5
|
||||
|
||||
ttk::style element create Treeheading.cell \
|
||||
image [list $I(tree-basic) \
|
||||
pressed $I(tree-pressed)
|
||||
] -border 5 -padding 4 -sticky ewns
|
||||
|
||||
ttk::style element create Treeitem.indicator \
|
||||
image [list $I(right) \
|
||||
user2 $I(empty) \
|
||||
user1 $I(down) \
|
||||
] -width 26 -sticky {}
|
||||
|
||||
ttk::style configure Treeview -background $colors(-bg)
|
||||
ttk::style configure Treeview.Item -padding {2 0 0 0}
|
||||
ttk::style map Treeview \
|
||||
-background [list selected $colors(-selectbg)] \
|
||||
-foreground [list selected $colors(-selectfg)]
|
||||
|
||||
# Panedwindow
|
||||
# Insane hack to remove clam's ugly sash
|
||||
ttk::style configure Sash -gripcount 0
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 330 B |
|
After Width: | Height: | Size: 357 B |
|
After Width: | Height: | Size: 405 B |
|
After Width: | Height: | Size: 346 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 482 B |
|
After Width: | Height: | Size: 423 B |
|
After Width: | Height: | Size: 453 B |
|
After Width: | Height: | Size: 346 B |
|
After Width: | Height: | Size: 310 B |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 437 B |
|
After Width: | Height: | Size: 470 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 248 B |
|
After Width: | Height: | Size: 297 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 261 B |
|
After Width: | Height: | Size: 130 B |
|
After Width: | Height: | Size: 154 B |
|
After Width: | Height: | Size: 156 B |
|
After Width: | Height: | Size: 154 B |
|
After Width: | Height: | Size: 410 B |
|
After Width: | Height: | Size: 677 B |
|
After Width: | Height: | Size: 736 B |
|
After Width: | Height: | Size: 668 B |
|
After Width: | Height: | Size: 587 B |
|
After Width: | Height: | Size: 644 B |
|
After Width: | Height: | Size: 629 B |
|
After Width: | Height: | Size: 561 B |
|
After Width: | Height: | Size: 625 B |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 505 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 289 B |
|
After Width: | Height: | Size: 319 B |
|
After Width: | Height: | Size: 286 B |
|
After Width: | Height: | Size: 297 B |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 249 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 260 B |
|
After Width: | Height: | Size: 302 B |
|
After Width: | Height: | Size: 267 B |
|
After Width: | Height: | Size: 280 B |
|
After Width: | Height: | Size: 295 B |
|
After Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 277 B |
|
After Width: | Height: | Size: 149 B |
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 158 B |
|
After Width: | Height: | Size: 158 B |
|
After Width: | Height: | Size: 158 B |
@@ -0,0 +1,537 @@
|
||||
# Copyright (c) 2021 rdbende <rdbende@gmail.com>
|
||||
|
||||
# The Azure theme is a beautiful modern ttk theme inspired by Microsoft's fluent design.
|
||||
|
||||
package require Tk 8.6
|
||||
|
||||
namespace eval ttk::theme::azure-light {
|
||||
variable version 2.0
|
||||
package provide ttk::theme::azure-light $version
|
||||
|
||||
ttk::style theme create azure-light -parent clam -settings {
|
||||
proc load_images {imgdir} {
|
||||
variable I
|
||||
foreach file [glob -directory $imgdir *.png] {
|
||||
set img [file tail [file rootname $file]]
|
||||
set I($img) [image create photo -file $file -format png]
|
||||
}
|
||||
}
|
||||
|
||||
load_images [file join [file dirname [info script]] light]
|
||||
|
||||
array set colors {
|
||||
-fg "#000000"
|
||||
-bg "#ffffff"
|
||||
-disabledfg "#737373"
|
||||
-disabledbg "#ffffff"
|
||||
-selectfg "#ffffff"
|
||||
-selectbg "#007fff"
|
||||
}
|
||||
|
||||
ttk::style layout TButton {
|
||||
Button.button -children {
|
||||
Button.padding -children {
|
||||
Button.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Toolbutton {
|
||||
Toolbutton.button -children {
|
||||
Toolbutton.padding -children {
|
||||
Toolbutton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TMenubutton {
|
||||
Menubutton.button -children {
|
||||
Menubutton.padding -children {
|
||||
Menubutton.indicator -side right
|
||||
Menubutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TOptionMenu {
|
||||
OptionMenu.button -children {
|
||||
OptionMenu.padding -children {
|
||||
OptionMenu.indicator -side right
|
||||
OptionMenu.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Accent.TButton {
|
||||
AccentButton.button -children {
|
||||
AccentButton.padding -children {
|
||||
AccentButton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TCheckbutton {
|
||||
Checkbutton.button -children {
|
||||
Checkbutton.padding -children {
|
||||
Checkbutton.indicator -side left
|
||||
Checkbutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Switch.TCheckbutton {
|
||||
Switch.button -children {
|
||||
Switch.padding -children {
|
||||
Switch.indicator -side left
|
||||
Switch.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Toggle.TButton {
|
||||
ToggleButton.button -children {
|
||||
ToggleButton.padding -children {
|
||||
ToggleButton.label -side left -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TRadiobutton {
|
||||
Radiobutton.button -children {
|
||||
Radiobutton.padding -children {
|
||||
Radiobutton.indicator -side left
|
||||
Radiobutton.label -side right -expand true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.TScrollbar {
|
||||
Vertical.Scrollbar.trough -sticky ns -children {
|
||||
Vertical.Scrollbar.thumb -expand true
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.TScrollbar {
|
||||
Horizontal.Scrollbar.trough -sticky ew -children {
|
||||
Horizontal.Scrollbar.thumb -expand true
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TCombobox {
|
||||
Combobox.field -sticky nswe -children {
|
||||
Combobox.padding -expand true -sticky nswe -children {
|
||||
Combobox.textarea -sticky nswe
|
||||
}
|
||||
}
|
||||
Combobox.button -side right -sticky ns -children {
|
||||
Combobox.arrow -sticky nsew
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TSpinbox {
|
||||
Spinbox.field -sticky nsew -children {
|
||||
Spinbox.padding -expand true -sticky nswe -children {
|
||||
Spinbox.textarea -sticky nswe
|
||||
}
|
||||
|
||||
}
|
||||
Spinbox.button -side right -sticky ns -children {
|
||||
null -side right -children {
|
||||
Spinbox.uparrow -side top
|
||||
Spinbox.downarrow -side bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.TSeparator {
|
||||
Horizontal.separator -sticky nswe
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.TSeparator {
|
||||
Vertical.separator -sticky nswe
|
||||
}
|
||||
|
||||
ttk::style layout Horizontal.Tick.TScale {
|
||||
Horizontal.TickScale.trough -sticky ew -children {
|
||||
Horizontal.TickScale.slider -sticky w
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Vertical.Tick.TScale {
|
||||
Vertical.TickScale.trough -sticky ns -children {
|
||||
Vertical.TickScale.slider -sticky n
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Card.TFrame {
|
||||
Card.field {
|
||||
Card.padding -expand 1
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TLabelframe {
|
||||
Labelframe.border {
|
||||
Labelframe.padding -expand 1 -children {
|
||||
Labelframe.label -side right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout TNotebook.Tab {
|
||||
Notebook.tab -children {
|
||||
Notebook.padding -side top -children {
|
||||
Notebook.label -side top -sticky {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttk::style layout Treeview.Item {
|
||||
Treeitem.padding -sticky nswe -children {
|
||||
Treeitem.indicator -side left -sticky {}
|
||||
Treeitem.image -side left -sticky {}
|
||||
Treeitem.text -side left -sticky {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Elements
|
||||
|
||||
# Button
|
||||
ttk::style configure TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create Button.button image \
|
||||
[list $I(rect-basic) \
|
||||
{selected disabled} $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
selected $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Toolbutton
|
||||
ttk::style configure Toolbutton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create Toolbutton.button image \
|
||||
[list $I(empty) \
|
||||
{selected disabled} $I(empty) \
|
||||
disabled $I(empty) \
|
||||
selected $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(rect-basic) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Menubutton
|
||||
ttk::style configure TMenubutton -padding {8 4 4 4}
|
||||
|
||||
ttk::style element create Menubutton.button \
|
||||
image [list $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
ttk::style element create Menubutton.indicator \
|
||||
image [list $I(down) \
|
||||
active $I(down) \
|
||||
pressed $I(down) \
|
||||
disabled $I(down) \
|
||||
] -width 15 -sticky e
|
||||
|
||||
# OptionMenu
|
||||
ttk::style configure TOptionMenu -padding {8 4 4 4}
|
||||
|
||||
ttk::style element create OptionMenu.button \
|
||||
image [list $I(rect-basic) \
|
||||
disabled $I(rect-basic) \
|
||||
pressed $I(rect-basic) \
|
||||
active $I(button-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
ttk::style element create OptionMenu.indicator \
|
||||
image [list $I(down) \
|
||||
active $I(down) \
|
||||
pressed $I(down) \
|
||||
disabled $I(down) \
|
||||
] -width 15 -sticky e
|
||||
|
||||
# AccentButton
|
||||
ttk::style configure Accent.TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create AccentButton.button image \
|
||||
[list $I(rect-accent) \
|
||||
{selected disabled} $I(rect-accent-hover) \
|
||||
disabled $I(rect-accent-hover) \
|
||||
selected $I(rect-accent) \
|
||||
pressed $I(rect-accent) \
|
||||
active $I(rect-accent-hover) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Checkbutton
|
||||
ttk::style configure TCheckbutton -padding 4
|
||||
|
||||
ttk::style element create Checkbutton.indicator image \
|
||||
[list $I(box-basic) \
|
||||
{alternate disabled} $I(check-tri-basic) \
|
||||
{selected disabled} $I(check-basic) \
|
||||
disabled $I(box-basic) \
|
||||
{pressed alternate} $I(check-tri-hover) \
|
||||
{active alternate} $I(check-tri-hover) \
|
||||
alternate $I(check-tri-accent) \
|
||||
{pressed selected} $I(check-hover) \
|
||||
{active selected} $I(check-hover) \
|
||||
selected $I(check-accent) \
|
||||
{pressed !selected} $I(rect-hover) \
|
||||
active $I(box-hover) \
|
||||
] -width 26 -sticky w
|
||||
|
||||
# Switch
|
||||
ttk::style element create Switch.indicator image \
|
||||
[list $I(off-basic) \
|
||||
{selected disabled} $I(on-basic) \
|
||||
disabled $I(off-basic) \
|
||||
{pressed selected} $I(on-hover) \
|
||||
{active selected} $I(on-hover) \
|
||||
selected $I(on-accent) \
|
||||
{pressed !selected} $I(off-hover) \
|
||||
active $I(off-hover) \
|
||||
] -width 46 -sticky w
|
||||
|
||||
# ToggleButton
|
||||
ttk::style configure Toggle.TButton -padding {8 4 8 4} -width -10 -anchor center
|
||||
|
||||
ttk::style element create ToggleButton.button image \
|
||||
[list $I(rect-basic) \
|
||||
{selected disabled} $I(rect-accent-hover) \
|
||||
disabled $I(rect-basic) \
|
||||
{pressed selected} $I(rect-basic) \
|
||||
{active selected} $I(rect-accent) \
|
||||
selected $I(rect-accent) \
|
||||
{pressed !selected} $I(rect-accent) \
|
||||
active $I(rect-basic) \
|
||||
] -border 4 -sticky ewns
|
||||
|
||||
# Radiobutton
|
||||
ttk::style configure TRadiobutton -padding 4
|
||||
|
||||
ttk::style element create Radiobutton.indicator image \
|
||||
[list $I(outline-basic) \
|
||||
{alternate disabled} $I(radio-tri-basic) \
|
||||
{selected disabled} $I(radio-basic) \
|
||||
disabled $I(outline-basic) \
|
||||
{pressed alternate} $I(radio-tri-hover) \
|
||||
{active alternate} $I(radio-tri-hover) \
|
||||
alternate $I(radio-tri-accent) \
|
||||
{pressed selected} $I(radio-hover) \
|
||||
{active selected} $I(radio-hover) \
|
||||
selected $I(radio-accent) \
|
||||
{pressed !selected} $I(circle-hover) \
|
||||
active $I(outline-hover) \
|
||||
] -width 26 -sticky w
|
||||
|
||||
# Scrollbar
|
||||
ttk::style element create Horizontal.Scrollbar.trough image $I(hor-basic) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Horizontal.Scrollbar.thumb \
|
||||
image [list $I(hor-accent) \
|
||||
disabled $I(hor-basic) \
|
||||
pressed $I(hor-hover) \
|
||||
active $I(hor-hover) \
|
||||
] -sticky ew
|
||||
|
||||
ttk::style element create Vertical.Scrollbar.trough image $I(vert-basic) \
|
||||
-sticky ns
|
||||
|
||||
ttk::style element create Vertical.Scrollbar.thumb \
|
||||
image [list $I(vert-accent) \
|
||||
disabled $I(vert-basic) \
|
||||
pressed $I(vert-hover) \
|
||||
active $I(vert-hover) \
|
||||
] -sticky ns
|
||||
|
||||
# Scale
|
||||
ttk::style element create Horizontal.Scale.trough image $I(scale-hor) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Horizontal.Scale.slider \
|
||||
image [list $I(circle-accent) \
|
||||
disabled $I(circle-basic) \
|
||||
pressed $I(circle-hover) \
|
||||
active $I(circle-hover) \
|
||||
] -sticky {}
|
||||
|
||||
ttk::style element create Vertical.Scale.trough image $I(scale-vert) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Vertical.Scale.slider \
|
||||
image [list $I(circle-accent) \
|
||||
disabled $I(circle-basic) \
|
||||
pressed $I(circle-hover) \
|
||||
active $I(circle-hover) \
|
||||
] -sticky {}
|
||||
|
||||
# Tickscale
|
||||
ttk::style element create Horizontal.TickScale.trough image $I(scale-hor) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Horizontal.TickScale.slider \
|
||||
image [list $I(tick-hor-accent) \
|
||||
disabled $I(tick-hor-basic) \
|
||||
pressed $I(tick-hor-hover) \
|
||||
active $I(tick-hor-hover) \
|
||||
] -sticky {}
|
||||
|
||||
ttk::style element create Vertical.TickScale.trough image $I(scale-vert) \
|
||||
-border 5 -padding 0
|
||||
|
||||
ttk::style element create Vertical.TickScale.slider \
|
||||
image [list $I(tick-vert-accent) \
|
||||
disabled $I(tick-vert-basic) \
|
||||
pressed $I(tick-vert-hover) \
|
||||
active $I(tick-vert-hover) \
|
||||
] -sticky {}
|
||||
|
||||
# Progressbar
|
||||
ttk::style element create Horizontal.Progressbar.trough image $I(hor-basic) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Horizontal.Progressbar.pbar image $I(hor-accent) \
|
||||
-sticky ew
|
||||
|
||||
ttk::style element create Vertical.Progressbar.trough image $I(vert-basic) \
|
||||
-sticky ns
|
||||
|
||||
ttk::style element create Vertical.Progressbar.pbar image $I(vert-accent) \
|
||||
-sticky ns
|
||||
|
||||
# Entry
|
||||
ttk::style element create Entry.field \
|
||||
image [list $I(box-basic) \
|
||||
{focus hover} $I(box-accent) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8} -sticky news
|
||||
|
||||
# Combobox
|
||||
ttk::style map TCombobox -selectbackground [list \
|
||||
{!focus} $colors(-selectbg) \
|
||||
{readonly hover} $colors(-selectbg) \
|
||||
{readonly focus} $colors(-selectbg) \
|
||||
]
|
||||
|
||||
ttk::style map TCombobox -selectforeground [list \
|
||||
{!focus} $colors(-selectfg) \
|
||||
{readonly hover} $colors(-selectfg) \
|
||||
{readonly focus} $colors(-selectfg) \
|
||||
]
|
||||
|
||||
ttk::style element create Combobox.field \
|
||||
image [list $I(box-basic) \
|
||||
{readonly disabled} $I(rect-basic) \
|
||||
{readonly pressed} $I(rect-basic) \
|
||||
{readonly focus hover} $I(button-hover) \
|
||||
{readonly focus} $I(button-hover) \
|
||||
{readonly hover} $I(button-hover) \
|
||||
{focus hover} $I(box-accent) \
|
||||
readonly $I(rect-basic) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8}
|
||||
|
||||
ttk::style element create Combobox.button \
|
||||
image [list $I(combo-button-basic) \
|
||||
{!readonly focus} $I(combo-button-focus) \
|
||||
{readonly focus} $I(combo-button-hover) \
|
||||
{readonly hover} $I(combo-button-hover)
|
||||
] -border 5 -padding {2 6 6 6}
|
||||
|
||||
ttk::style element create Combobox.arrow image $I(down) \
|
||||
-width 15 -sticky e
|
||||
|
||||
# Spinbox
|
||||
ttk::style element create Spinbox.field \
|
||||
image [list $I(box-basic) \
|
||||
invalid $I(box-invalid) \
|
||||
disabled $I(box-basic) \
|
||||
focus $I(box-accent) \
|
||||
hover $I(box-hover) \
|
||||
] -border 5 -padding {8} -sticky news
|
||||
|
||||
ttk::style element create Spinbox.uparrow \
|
||||
image [list $I(up) \
|
||||
disabled $I(up) \
|
||||
pressed $I(up-accent) \
|
||||
active $I(up-accent) \
|
||||
] -border 4 -width 15 -sticky e
|
||||
|
||||
ttk::style element create Spinbox.downarrow \
|
||||
image [list $I(down) \
|
||||
disabled $I(down) \
|
||||
pressed $I(down-accent) \
|
||||
active $I(down-accent) \
|
||||
] -border 4 -width 15 -sticky e
|
||||
|
||||
ttk::style element create Spinbox.button \
|
||||
image [list $I(combo-button-basic) \
|
||||
{!readonly focus} $I(combo-button-focus) \
|
||||
{readonly focus} $I(combo-button-hover) \
|
||||
{readonly hover} $I(combo-button-hover)
|
||||
] -border 5 -padding {2 6 6 6}
|
||||
|
||||
# Sizegrip
|
||||
ttk::style element create Sizegrip.sizegrip image $I(size) \
|
||||
-sticky ewns
|
||||
|
||||
# Separator
|
||||
ttk::style element create Horizontal.separator image $I(separator)
|
||||
|
||||
ttk::style element create Vertical.separator image $I(separator)
|
||||
|
||||
# Card
|
||||
ttk::style element create Card.field image $I(card) \
|
||||
-border 10 -padding 4 -sticky news
|
||||
|
||||
# Labelframe
|
||||
ttk::style element create Labelframe.border image $I(card) \
|
||||
-border 5 -padding 4 -sticky news
|
||||
|
||||
# Notebook
|
||||
ttk::style element create Notebook.client \
|
||||
image $I(notebook) -border 5
|
||||
|
||||
ttk::style element create Notebook.tab \
|
||||
image [list $I(tab-disabled) \
|
||||
selected $I(tab-basic) \
|
||||
active $I(tab-hover) \
|
||||
] -border 5 -padding {14 4}
|
||||
|
||||
# Treeview
|
||||
ttk::style element create Treeview.field image $I(card) \
|
||||
-border 5
|
||||
|
||||
ttk::style element create Treeheading.cell \
|
||||
image [list $I(tree-basic) \
|
||||
pressed $I(tree-pressed)
|
||||
] -border 5 -padding 4 -sticky ewns
|
||||
|
||||
ttk::style element create Treeitem.indicator \
|
||||
image [list $I(right) \
|
||||
user2 $I(empty) \
|
||||
user1 $I(down) \
|
||||
] -width 26 -sticky {}
|
||||
|
||||
ttk::style configure Treeview -background $colors(-bg)
|
||||
ttk::style configure Treeview.Item -padding {2 0 0 0}
|
||||
ttk::style map Treeview \
|
||||
-background [list selected #ccc] \
|
||||
-foreground [list selected $colors(-fg)]
|
||||
|
||||
# Panedwindow
|
||||
# Insane hack to remove clam's ugly sash
|
||||
ttk::style configure Sash -gripcount 0
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 346 B |
|
After Width: | Height: | Size: 319 B |
|
After Width: | Height: | Size: 329 B |
|
After Width: | Height: | Size: 285 B |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 390 B |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 314 B |
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 319 B |
|
After Width: | Height: | Size: 440 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 429 B |
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 254 B |
|
After Width: | Height: | Size: 299 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 130 B |
|
After Width: | Height: | Size: 153 B |
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 154 B |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 547 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 538 B |
|
After Width: | Height: | Size: 649 B |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 598 B |
|
After Width: | Height: | Size: 554 B |