from tkinter import ttk, messagebox
def __init__(self, root):
self.root.title("CDS Request Builder")
var_frame = ttk.Labelframe(root, text="Variable")
var_frame.pack(padx=10, pady=5, fill="x")
self.variable_var = tk.StringVar(value="2m_temperature")
ttk.Label(var_frame, text="Variable:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
variable_combo = ttk.Combobox(var_frame, textvariable=self.variable_var,
values=["2m_temperature", "2m_dewpoint_temperature", "total_precipitation", "snowfall"],
state="readonly", width=20)
variable_combo.grid(row=0, column=1, padx=5, pady=5, sticky="w")
# --- YEAR GRID (Radiobuttons in a scrollable canvas) ---
year_container = ttk.Labelframe(root, text="Year")
year_container.pack(padx=10, pady=5, fill="both", expand=True)
# Canvas and scrollbar for years
self.year_canvas = tk.Canvas(year_container, height=100)
self.year_canvas.pack(side="left", fill="both", expand=True)
year_scrollbar = ttk.Scrollbar(year_container, orient="vertical", command=self.year_canvas.yview)
year_scrollbar.pack(side="right", fill="y")
self.year_canvas.configure(yscrollcommand=year_scrollbar.set)
# Frame inside canvas for radiobuttons
self.years_frame = ttk.Frame(self.year_canvas)
self.year_canvas.create_window((0, 0), window=self.years_frame, anchor="nw")
self.years_frame.bind("<Configure>", lambda e: self.year_canvas.configure(scrollregion=self.year_canvas.bbox("all")))
# Radiobutton variable for year (only one year can be selected)
self.year_var = tk.StringVar(value="2025")
years_list = [str(y) for y in range(self.start_year, self.end_year + 1)]
num_cols = 12 # Increase columns to use minimal vertical space
for idx, yr in enumerate(years_list):
rb = ttk.Radiobutton(self.years_frame, text=yr, variable=self.year_var, value=yr)
rb.grid(row=row, column=col, padx=5, pady=2, sticky="w")
# Optional: Button to clear year selection (set to empty string)
clear_years_btn = ttk.Button(self.years_frame, text="Clear", command=lambda: self.year_var.set(""))
clear_years_btn.grid(row=(len(years_list) // num_cols) + 1, column=0, columnspan=num_cols, sticky="w", padx=5, pady=5)
# --- MONTH GRID (Checkbuttons) ---
month_container = ttk.Labelframe(root, text="Month")
month_container.pack(padx=10, pady=5, fill="x")
# We'll show months as zero-padded indices ("01", "02", ... "12")
var = tk.BooleanVar(value=False)
cb = ttk.Checkbutton(month_container, text=month_label, variable=var)
cb.grid(row=0, column=m-1, padx=5, pady=2, sticky="w")
# Optional: Button to clear month selection
clear_months_btn = ttk.Button(month_container, text="Clear all", command=self.clear_all_months)
clear_months_btn.grid(row=1, column=0, columnspan=12, pady=5, sticky="w")
day_frame = ttk.Labelframe(root, text="Day")
day_frame.pack(padx=10, pady=5, fill="x")
self.day_entry = ttk.Entry(day_frame, width=4)
self.day_entry.insert(0, "01")
self.day_entry.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.all_days_var = tk.BooleanVar(value=False)
all_days_cb = ttk.Checkbutton(day_frame, text="All Days", variable=self.all_days_var, command=self.toggle_day_entry)
all_days_cb.grid(row=0, column=1, padx=5, pady=5, sticky="w")
time_frame = ttk.Labelframe(root, text="Time")
time_frame.pack(padx=10, pady=5, fill="x")
self.time_entry = ttk.Entry(time_frame, width=6)
self.time_entry.insert(0, "12:00")
self.time_entry.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.all_hours_var = tk.BooleanVar(value=False)
all_hours_cb = ttk.Checkbutton(time_frame, text="All Hours", variable=self.all_hours_var, command=self.toggle_time_entry)
all_hours_cb.grid(row=0, column=1, padx=5, pady=5, sticky="w")
format_frame = ttk.Labelframe(root, text="Data Format")
format_frame.pack(padx=10, pady=5, fill="x")
self.data_format_var = tk.StringVar(value="grib") # or "netcdf"
ttk.Label(format_frame, text="Data Format:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
format_combo = ttk.Combobox(format_frame, textvariable=self.data_format_var, values=["grib", "netcdf"], state="readonly", width=10)
format_combo.grid(row=0, column=1, padx=5, pady=5, sticky="w")
area_frame = ttk.Labelframe(root, text="Area (N, W, S, E)")
area_frame.pack(padx=10, pady=5, fill="x")
self.north_var = tk.StringVar(value="-20.4281081")
self.west_var = tk.StringVar(value="-43.28306098")
self.south_var = tk.StringVar(value="-21.23747331")
self.east_var = tk.StringVar(value="-42.41469862")
ttk.Label(area_frame, text="North:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
ttk.Entry(area_frame, textvariable=self.north_var, width=10).grid(row=0, column=1, padx=5, pady=5, sticky="w")
ttk.Label(area_frame, text="West:").grid(row=0, column=2, padx=5, pady=5, sticky="w")
ttk.Entry(area_frame, textvariable=self.west_var, width=10).grid(row=0, column=3, padx=5, pady=5, sticky="w")
ttk.Label(area_frame, text="South:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
ttk.Entry(area_frame, textvariable=self.south_var, width=10).grid(row=1, column=1, padx=5, pady=5, sticky="w")
ttk.Label(area_frame, text="East:").grid(row=1, column=2, padx=5, pady=5, sticky="w")
ttk.Entry(area_frame, textvariable=self.east_var, width=10).grid(row=1, column=3, padx=5, pady=5, sticky="w")
# --- DOWNLOAD FORMAT (fixed) ---
self.download_format = "unarchived"
# --- GENERATE BUTTON ---
generate_button = ttk.Button(root, text="Generate CDS Request", command=self.generate_request)
generate_button.pack(pady=10)
self.output_text = tk.Text(root, width=80, height=10)
self.output_text.pack(padx=10, pady=5)
# --- Utility Methods ---
def clear_all_months(self):
for m in self.month_vars:
self.month_vars[m].set(False)
def toggle_day_entry(self):
if self.all_days_var.get():
self.day_entry.config(state="disabled")
self.day_entry.config(state="normal")
def toggle_time_entry(self):
if self.all_hours_var.get():
self.time_entry.config(state="disabled")
self.time_entry.config(state="normal")
def generate_request(self):
# variable: always a list
variable_list = [self.variable_var.get()]
# year: single string (from the radiobutton grid)
year_str = self.year_var.get().strip()
messagebox.showerror("Error", "Please select a year.")
# month: checkbuttons return a list of zero-padded strings for selected months
selected_months = [f"{m:02d}" for m, var in self.month_vars.items() if var.get()]
messagebox.showerror("Error", "Please select at least one month.")
# day: either all days or single day in a list
if self.all_days_var.get():
day_list = [f"{d:02d}" for d in range(1, 32)]
day_str = self.day_entry.get().strip().zfill(2)
# time: either all hours or a single time in a list
if self.all_hours_var.get():
time_list = [f"{h:02d}:00" for h in range(24)]
time_list = [self.time_entry.get().strip()]
# data_format: from combobox
data_format_str = self.data_format_var.get()
# area: parse the 4 entries as floats
area_vals = [float(self.north_var.get().strip()),
float(self.west_var.get().strip()),
float(self.south_var.get().strip()),
float(self.east_var.get().strip())]
messagebox.showerror("Error", "Please enter valid numeric values for area.")
# Build the final request dictionary
"variable": variable_list, # e.g. ['2m_temperature']
"year": year_str, # e.g. '2025'
"month": selected_months, # e.g. ['01'] or multiple months as ['01', '02']
"day": day_list, # e.g. ['01', '02', ... '31'] or single day in a list
"time": time_list, # e.g. ['00:00', '01:00', ... '23:00'] or a single time in a list
"data_format": data_format_str, # 'grib' or 'netcdf'
"area": area_vals, # list of 4 floats
"download_format": self.download_format # 'unarchived'
# Output the JSON representation
self.output_text.delete("1.0", tk.END)
self.output_text.insert(tk.END, json.dumps(request, indent=4))
messagebox.showinfo("Request Generated", "Your CDS request has been generated.")
if __name__ == "__main__":
root.geometry("1000x800")