|
| 1 | +import tkinter as tk |
| 2 | +from tkinter import ttk, messagebox |
| 3 | +import threading |
| 4 | +import random |
| 5 | +import time |
| 6 | +from typing import List, Protocol |
| 7 | + |
| 8 | +class Observer(Protocol): |
| 9 | + def update(self, temperature: float, humidity: float, pressure: float): |
| 10 | + ... |
| 11 | + |
| 12 | +class WeatherStation: |
| 13 | + def __init__(self): |
| 14 | + self._observers: List[Observer] = [] |
| 15 | + self._temperature = 25.0 |
| 16 | + self._humidity = 50.0 |
| 17 | + self._pressure = 1013.0 |
| 18 | + |
| 19 | + def register(self, observer: Observer): |
| 20 | + if observer not in self._observers: |
| 21 | + self._observers.append(observer) |
| 22 | + |
| 23 | + def unregister(self, observer: Observer): |
| 24 | + if observer in self._observers: |
| 25 | + self._observers.remove(observer) |
| 26 | + |
| 27 | + def notify(self): |
| 28 | + for observer in self._observers: |
| 29 | + observer.update(self._temperature, self._humidity, self._pressure) |
| 30 | + |
| 31 | + def set_weather(self, temperature: float, humidity: float, pressure: float): |
| 32 | + self._temperature = temperature |
| 33 | + self._humidity = humidity |
| 34 | + self._pressure = pressure |
| 35 | + self.notify() |
| 36 | + |
| 37 | + @property |
| 38 | + def weather(self): |
| 39 | + return self._temperature, self._humidity, self._pressure |
| 40 | + |
| 41 | +class PhoneDisplay(tk.LabelFrame): |
| 42 | + def __init__(self, master, **kwargs): |
| 43 | + super().__init__(master, text="Phone Display", padx=10, pady=10, **kwargs) |
| 44 | + self.label = tk.Label(self, text="No data", font=("Arial", 12)) |
| 45 | + self.label.pack() |
| 46 | + |
| 47 | + def update(self, temperature: float, humidity: float, pressure: float): |
| 48 | + self.label.config(text=f"Temp: {temperature}°C\nHumidity: {humidity}%\nPressure: {pressure} hPa") |
| 49 | + |
| 50 | +class WindowDisplay(tk.LabelFrame): |
| 51 | + def __init__(self, master, **kwargs): |
| 52 | + super().__init__(master, text="Window Display", padx=10, pady=10, **kwargs) |
| 53 | + self.label = tk.Label(self, text="No data", font=("Arial", 12)) |
| 54 | + self.label.pack() |
| 55 | + |
| 56 | + def update(self, temperature: float, humidity: float, pressure: float): |
| 57 | + self.label.config(text=f"Temp: {temperature}°C\nHumidity: {humidity}%\nPressure: {pressure} hPa") |
| 58 | + |
| 59 | +class WeatherGUI(tk.Tk): |
| 60 | + def __init__(self): |
| 61 | + super().__init__() |
| 62 | + self.title("Weather Monitoring System") |
| 63 | + self.geometry("500x400") |
| 64 | + self.resizable(False, False) |
| 65 | + self.station = WeatherStation() |
| 66 | + self.simulation_running = False |
| 67 | + self.sim_thread = None |
| 68 | + |
| 69 | + # Observer widgets |
| 70 | + self.phone_display = PhoneDisplay(self) |
| 71 | + self.window_display = WindowDisplay(self) |
| 72 | + self.phone_display.place(x=30, y=120, width=200, height=120) |
| 73 | + self.window_display.place(x=270, y=120, width=200, height=120) |
| 74 | + |
| 75 | + # Controls |
| 76 | + self.create_controls() |
| 77 | + |
| 78 | + def create_controls(self): |
| 79 | + frame = tk.LabelFrame(self, text="Weather Controls", padx=10, pady=10) |
| 80 | + frame.place(x=30, y=10, width=440, height=90) |
| 81 | + |
| 82 | + tk.Label(frame, text="Temperature (°C):").grid(row=0, column=0) |
| 83 | + tk.Label(frame, text="Humidity (%):").grid(row=0, column=2) |
| 84 | + tk.Label(frame, text="Pressure (hPa):").grid(row=0, column=4) |
| 85 | + |
| 86 | + self.temp_var = tk.DoubleVar(value=25) |
| 87 | + self.hum_var = tk.DoubleVar(value=50) |
| 88 | + self.pres_var = tk.DoubleVar(value=1013) |
| 89 | + |
| 90 | + tk.Entry(frame, textvariable=self.temp_var, width=6).grid(row=0, column=1) |
| 91 | + tk.Entry(frame, textvariable=self.hum_var, width=6).grid(row=0, column=3) |
| 92 | + tk.Entry(frame, textvariable=self.pres_var, width=8).grid(row=0, column=5) |
| 93 | + |
| 94 | + ttk.Button(frame, text="Update Weather", command=self.update_weather).grid(row=1, column=0, pady=8) |
| 95 | + ttk.Button(frame, text="Register Phone", command=self.register_phone).grid(row=1, column=1) |
| 96 | + ttk.Button(frame, text="Unregister Phone", command=self.unregister_phone).grid(row=1, column=2) |
| 97 | + ttk.Button(frame, text="Register Window", command=self.register_window).grid(row=1, column=3) |
| 98 | + ttk.Button(frame, text="Unregister Window", command=self.unregister_window).grid(row=1, column=4) |
| 99 | + self.sim_btn = ttk.Button(frame, text="Start Simulation", command=self.toggle_simulation) |
| 100 | + self.sim_btn.grid(row=1, column=5) |
| 101 | + |
| 102 | + def update_weather(self): |
| 103 | + try: |
| 104 | + t = self.temp_var.get() |
| 105 | + h = self.hum_var.get() |
| 106 | + p = self.pres_var.get() |
| 107 | + self.station.set_weather(t, h, p) |
| 108 | + except Exception as e: |
| 109 | + messagebox.showerror("Error", f"Invalid input: {e}") |
| 110 | + |
| 111 | + def register_phone(self): |
| 112 | + self.station.register(self.phone_display) |
| 113 | + messagebox.showinfo("Observer", "Phone Display registered.") |
| 114 | + |
| 115 | + def unregister_phone(self): |
| 116 | + self.station.unregister(self.phone_display) |
| 117 | + messagebox.showinfo("Observer", "Phone Display unregistered.") |
| 118 | + |
| 119 | + def register_window(self): |
| 120 | + self.station.register(self.window_display) |
| 121 | + messagebox.showinfo("Observer", "Window Display registered.") |
| 122 | + |
| 123 | + def unregister_window(self): |
| 124 | + self.station.unregister(self.window_display) |
| 125 | + messagebox.showinfo("Observer", "Window Display unregistered.") |
| 126 | + |
| 127 | + def toggle_simulation(self): |
| 128 | + if not self.simulation_running: |
| 129 | + self.simulation_running = True |
| 130 | + self.sim_btn.config(text="Stop Simulation") |
| 131 | + self.sim_thread = threading.Thread(target=self.simulate_weather, daemon=True) |
| 132 | + self.sim_thread.start() |
| 133 | + else: |
| 134 | + self.simulation_running = False |
| 135 | + self.sim_btn.config(text="Start Simulation") |
| 136 | + |
| 137 | + def simulate_weather(self): |
| 138 | + while self.simulation_running: |
| 139 | + t = round(random.uniform(15, 35), 1) |
| 140 | + h = round(random.uniform(30, 70), 1) |
| 141 | + p = round(random.uniform(990, 1030), 1) |
| 142 | + self.temp_var.set(t) |
| 143 | + self.hum_var.set(h) |
| 144 | + self.pres_var.set(p) |
| 145 | + self.station.set_weather(t, h, p) |
| 146 | + time.sleep(2) |
| 147 | + |
| 148 | +if __name__ == "__main__": |
| 149 | + app = WeatherGUI() |
| 150 | + app.mainloop() |
0 commit comments