From 2ce1fcb62ed17e0c0045a571335ba34588992389 Mon Sep 17 00:00:00 2001 From: Amit Chaudhary Date: Sun, 2 Jun 2024 10:02:25 +0530 Subject: [PATCH] isolate settings as class --- data/io.github.amit9838.mousam.gschema.xml | 2 +- po/POTFILES | 1 - src/backendWeather.py | 39 ++-- src/config.py | 75 +++++++ src/frontendCardAirPollution.py | 4 +- src/frontendCardDayNight.py | 4 +- src/frontendCardSquare.py | 4 +- src/frontendCurrentCond.py | 15 +- src/frontendForecast.py | 4 +- src/frontendHourlyDetails.py | 15 +- src/main.py | 5 +- src/meson.build | 2 +- src/mousam.py | 11 +- src/units.py | 5 - src/utils.py | 18 +- src/weatherData.py | 6 +- src/windowLocations.py | 237 ++++++++++++--------- src/windowPreferences.py | 31 +-- 18 files changed, 269 insertions(+), 209 deletions(-) create mode 100644 src/config.py delete mode 100644 src/units.py diff --git a/data/io.github.amit9838.mousam.gschema.xml b/data/io.github.amit9838.mousam.gschema.xml index dc1d28c..b70171e 100644 --- a/data/io.github.amit9838.mousam.gschema.xml +++ b/data/io.github.amit9838.mousam.gschema.xml @@ -28,7 +28,7 @@ Launch the app in maximized mode. - + "metric" Mesaurement sysytem to use - metric,imperial metric(km, °C), imperial(mile, °F) diff --git a/po/POTFILES b/po/POTFILES index 8773d93..6a251de 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -20,7 +20,6 @@ src/main.py src/Models.py src/mousam.in src/mousam.py -src/units.py src/utils.py src/weatherData.py src/windowAbout.py diff --git a/src/backendWeather.py b/src/backendWeather.py index 9bd5452..62addf5 100644 --- a/src/backendWeather.py +++ b/src/backendWeather.py @@ -1,7 +1,7 @@ import requests import datetime -from gi.repository import Gio +from .config import settings base_url = "https://api.open-meteo.com/v1/forecast" @@ -13,25 +13,20 @@ class Weather: """ def __init__(self) -> None: - global settings, measurement_type, temperature_unit, wind_speed_unit - settings = Gio.Settings(schema_id="io.github.amit9838.mousam") - measurement_type = settings.get_string("measure-type") - if measurement_type == "imperial": - temperature_unit = "fahrenheit" - wind_speed_unit = "mph" + global extend_url + extend_url = "" + if settings.unit == "imperial": + extend_url = f"&temperature_unit=fahrenheit&wind_speed_unit=mph" # Current Weather ============================================= - @staticmethod - def current_weather(latitude: float, longitude: float, **kwargs): + @classmethod + def current_weather(cls,latitude: float, longitude: float, **kwargs): url = base_url + f"?latitude={latitude}&longitude={longitude}" # Check for kwargs keyword parameters if "current" in kwargs: current_fields = ",".join(kwargs.get("current")) - url = url + f"¤t={current_fields}" - - if measurement_type == "imperial": - url += f"&temperature_unit={temperature_unit}&wind_speed_unit={wind_speed_unit}" + url = url + f"¤t={current_fields}" + extend_url try: url = url + f"&timeformat=unixtime" @@ -58,17 +53,14 @@ def _get_current_weather(self, lat, lon): return self.current_weather(lat, lon, current=current_args) # Hourly Forecast ============================================== - @staticmethod - def forecast_hourly(latitude: float, longitude: float, **kwargs): + @classmethod + def forecast_hourly(cls,latitude: float, longitude: float, **kwargs): url = base_url + f"?latitude={latitude}&longitude={longitude}" # Check for kwargs keyword parameters if "hourly" in kwargs: hourly_fields = ",".join(kwargs.get("hourly")) - url = url + f"&hourly={hourly_fields}" - - if measurement_type == "imperial": - url += f"&temperature_unit={temperature_unit}&wind_speed_unit={wind_speed_unit}" + url = url + f"&hourly={hourly_fields}" + extend_url try: url = url + f"&timeformat=unixtime" @@ -103,8 +95,8 @@ def _get_hourly_forecast(self, lat, lon): ) # Forecast daily ==================================================== - @staticmethod - def forecast_daily(latitude: float, longitude: float, **kwargs): + @classmethod + def forecast_daily(cls,latitude: float, longitude: float, **kwargs): url = base_url + f"?latitude={latitude}&longitude={longitude}" if "daily" in kwargs: hourly_fields = ",".join(kwargs.get("daily")) @@ -116,10 +108,7 @@ def forecast_daily(latitude: float, longitude: float, **kwargs): url = url + f"&start_date={kwargs.get('start_date')}" if "end_date" in kwargs: - url = url + f"&end_date={kwargs.get('end_date')}" - - if measurement_type == "imperial": - url += f"&temperature_unit={temperature_unit}&wind_speed_unit={wind_speed_unit}" + url = url + f"&end_date={kwargs.get('end_date')}" + extend_url try: url = url + f"&timeformat=unixtime" diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..19d5af1 --- /dev/null +++ b/src/config.py @@ -0,0 +1,75 @@ +from gi.repository import Gio + +class Settings: + _instance = None + APP_ID = "io.github.amit9838.mousam" + + def __new__(cls): + if cls._instance is None: + cls._instance = super(Settings, cls).__new__(cls) + cls._instance.init_settings() + return cls._instance + + def init_settings(self): + self.settings = Gio.Settings(self.APP_ID) + self._settings_map = { + "added_cities": "added-cities", + "selected_city": "selected-city", + "is_using_dynamic_bg": "use-gradient-bg", + "is_using_inch_for_prec": "use-inch-for-prec", + "should_launch_maximized": "launch-maximized", + "unit": "unit", + } + + @property + def added_cities(self): + return self.settings.get_strv(self._settings_map["added_cities"]) + + @added_cities.setter + def added_cities(self, value): + self.settings.set_strv(self._settings_map["added_cities"], value) + + @property + def selected_city(self): + return self.settings.get_string(self._settings_map["selected_city"]) + + @selected_city.setter + def selected_city(self, value): + self.settings.set_string(self._settings_map["selected_city"], value) + + @property + def is_using_dynamic_bg(self): + return self.settings.get_boolean(self._settings_map["is_using_dynamic_bg"]) + + @is_using_dynamic_bg.setter + def is_using_dynamic_bg(self, value): + self.settings.set_boolean(self._settings_map["is_using_dynamic_bg"], value) + + @property + def is_using_inch_for_prec(self): + return self.settings.get_boolean(self._settings_map["is_using_inch_for_prec"]) + + @is_using_inch_for_prec.setter + def is_using_inch_for_prec(self, value): + self.settings.set_boolean(self._settings_map["is_using_inch_for_prec"], value) + + @property + def should_launch_maximized(self): + return self.settings.get_boolean(self._settings_map["should_launch_maximized"]) + + @should_launch_maximized.setter + def should_launch_maximized(self, value): + self.settings.set_boolean(self._settings_map["should_launch_maximized"], value) + + @property + def unit(self): + return self.settings.get_string(self._settings_map["unit"]) + + @unit.setter + def unit(self, value): + self.settings.set_string(self._settings_map["unit"], value) + +def get_settings(): + return Settings() + +settings = get_settings() diff --git a/src/frontendCardAirPollution.py b/src/frontendCardAirPollution.py index 4f9aac7..0d6dc9a 100644 --- a/src/frontendCardAirPollution.py +++ b/src/frontendCardAirPollution.py @@ -6,7 +6,7 @@ from gi.repository import Gtk from .frontendUiDrawPollutionBar import PollutionBar -from .utils import is_dynamic_bg_enabled +from .config import settings class CardAirPollution: def __init__(self): @@ -36,7 +36,7 @@ def create_card(self): card.halign = Gtk.Align.FILL card.set_row_spacing(5) card.set_css_classes(["view", "card", "custom_card"]) - if is_dynamic_bg_enabled(): + if settings.is_using_dynamic_bg: card.add_css_class("transparent_5") # Main title of the card diff --git a/src/frontendCardDayNight.py b/src/frontendCardDayNight.py index cdfdb60..17a745a 100644 --- a/src/frontendCardDayNight.py +++ b/src/frontendCardDayNight.py @@ -5,11 +5,11 @@ from gi.repository import Gtk from .frontendUiDrawDayNight import * +from .config import settings from .utils import ( get_tz_offset_by_cord, get_cords, get_my_tz_offset_from_utc, - is_dynamic_bg_enabled, get_time_difference, ) @@ -70,7 +70,7 @@ def create_card(self): card.set_row_spacing(5) card.set_css_classes(["view", "card", "custom_card"]) - if is_dynamic_bg_enabled(): + if settings.is_using_dynamic_bg: card.add_css_class("transparent_5") # Main title of the card diff --git a/src/frontendCardSquare.py b/src/frontendCardSquare.py index cb41360..2f7d82a 100644 --- a/src/frontendCardSquare.py +++ b/src/frontendCardSquare.py @@ -1,7 +1,7 @@ from gi.repository import Gtk import gi from .constants import icons -from .utils import is_dynamic_bg_enabled +from .config import settings from .frontendUiDrawBar import * from .frontendUiDrawImageIcon import * @@ -53,7 +53,7 @@ def create_card(self): card.set_size_request(200, 150) card.set_css_classes(["view", "card", "custom_card"]) - if is_dynamic_bg_enabled(): + if settings.is_using_dynamic_bg: card.add_css_class("transparent_5") self.card = card diff --git a/src/frontendCurrentCond.py b/src/frontendCurrentCond.py index 17e5dfa..ef42751 100644 --- a/src/frontendCurrentCond.py +++ b/src/frontendCurrentCond.py @@ -1,6 +1,7 @@ import gi -from gi.repository import Gtk, Gio +from gi.repository import Gtk from .constants import icons, conditon +from .config import settings gi.require_version("Gtk", "4.0") gi.require_version("Adw", "1") @@ -51,7 +52,9 @@ def paint_ui(self): # Condition temperature main_temp_label = Gtk.Label( - label="{0:.0f} {1}".format(data.temperature_2m.get("data"),data.temperature_2m.get("unit")), + label="{0:.0f} {1}".format( + data.temperature_2m.get("data"), data.temperature_2m.get("unit") + ), halign=Gtk.Align.START, valign=Gtk.Align.START, ) @@ -64,15 +67,11 @@ def paint_ui(self): ) self.attach(box_right, 1, 0, 1, 1) - self.settings = Gio.Settings(schema_id="io.github.amit9838.mousam") - self.added_cities = self.settings.get_strv("added-cities") - self.selected_city = self.settings.get_string("selected-city") - self.selected_city_index = list( - map(lambda city: self.selected_city in city, self.added_cities) + map(lambda city: settings.selected_city in city, settings.added_cities) ).index(True) - city_arr = self.added_cities[self.selected_city_index].split(",") + city_arr = settings.added_cities[self.selected_city_index].split(",") # Delete lat,lon from the array del city_arr[-1] diff --git a/src/frontendForecast.py b/src/frontendForecast.py index 9beb7a9..82726f4 100644 --- a/src/frontendForecast.py +++ b/src/frontendForecast.py @@ -3,7 +3,7 @@ from gi.repository import Gtk from gettext import gettext as _, pgettext as C_ from .constants import icons -from .utils import is_dynamic_bg_enabled +from .config import settings gi.require_version('Gtk', '4.0') gi.require_version('Adw', '1') @@ -17,7 +17,7 @@ def __init__(self, *args, **kwargs): self.set_margin_start(10) self.set_margin_end(5) self.set_css_classes(['view', 'card', 'custom_card']) - if is_dynamic_bg_enabled(): + if settings.is_using_dynamic_bg: self.add_css_class("transparent_5") self.paint_ui() diff --git a/src/frontendHourlyDetails.py b/src/frontendHourlyDetails.py index 59f4e8e..c4efd7e 100644 --- a/src/frontendHourlyDetails.py +++ b/src/frontendHourlyDetails.py @@ -3,7 +3,7 @@ import time import gi import gettext -from gi.repository import Gtk, Gio +from gi.repository import Gtk gi.require_version("Gtk", "4.0") gi.require_version("Adw", "1") @@ -12,7 +12,7 @@ from .constants import icons, icon_loc from .frontendUiDrawImageIcon import DrawImage from .frontendUiDrawbarLine import DrawBar -from .utils import is_dynamic_bg_enabled +from .config import settings icon_loc += "arrow.svg" @@ -22,8 +22,10 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_hexpand(True) self.set_css_classes(["view", "card", "custom_card"]) - if is_dynamic_bg_enabled(): + + if settings.is_using_dynamic_bg: self.add_css_class("transparent_5") + self.set_margin_top(20) self.set_margin_start(5) self.paint_ui() @@ -114,12 +116,9 @@ def create_stack_page(self, page_name): unit_label.set_text("") # Precipitation page - settings = Gio.Settings(schema_id="io.github.amit9838.mousam") - use_inch_for_prec = settings.get_boolean("use-inch-for-prec") - max_prec = max(hourly_data.precipitation.get("data")[:24]) unit = hourly_data.precipitation.get("unit") - if use_inch_for_prec: + if settings.is_using_inch_for_prec: max_prec = max_prec / 25.4 unit = "inch" @@ -245,7 +244,7 @@ def create_stack_page(self, page_name): elif page_name == "prec": bar_obj = None prec = hourly_data.precipitation.get("data")[i] - if use_inch_for_prec: + if settings.is_using_inch_for_prec: prec = hourly_data.precipitation.get("data")[i] / 25.4 if max_prec == 0: bar_obj = DrawBar(0) diff --git a/src/main.py b/src/main.py index dbd70f1..6bc0bc0 100644 --- a/src/main.py +++ b/src/main.py @@ -26,6 +26,7 @@ from gi.repository import Gtk, Gio, Adw, Gdk from .mousam import WeatherMainWindow +from .config import settings class WeatherApplication(Adw.Application): """The main application singleton class.""" @@ -34,7 +35,6 @@ def __init__(self): super().__init__(application_id='io.github.amit9838.mousam', flags=Gio.ApplicationFlags.DEFAULT_FLAGS) self.create_action('quit', lambda *_: self.quit(), ['q']) - self.settings = Gio.Settings(schema_id="io.github.amit9838.mousam") self.main_window = None self.set_accels_for_action(f"win.preferences", ['comma']) @@ -52,12 +52,11 @@ def do_activate(self): css_provider.load_from_data(css,len(css)) Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), css_provider, Priority) - launch_maximized = self.settings.get_boolean("launch-maximized") if not win: win = WeatherMainWindow(application=self) - if launch_maximized: + if settings.should_launch_maximized: win.maximize() win.present() diff --git a/src/meson.build b/src/meson.build index 2337e58..f4845ab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -50,8 +50,8 @@ mousam_sources = [ 'frontendUiDrawPollutionBar.py', 'constants.py', - 'units.py', 'utils.py', + 'config.py', 'windowAbout.py', 'windowPreferences.py', 'windowLocations.py', diff --git a/src/mousam.py b/src/mousam.py index dfe77fc..a717217 100644 --- a/src/mousam.py +++ b/src/mousam.py @@ -22,6 +22,7 @@ from .frontendCardSquare import CardSquare from .frontendCardDayNight import CardDayNight from .frontendCardAirPollution import CardAirPollution +from .config import settings from .weatherData import ( fetch_current_weather, fetch_hourly_forecast, @@ -38,7 +39,6 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.main_window = self - self.settings = Gio.Settings(schema_id="io.github.amit9838.mousam") self.set_default_size(1160, 818) self.set_title("") self._use_dynamic_bg() @@ -232,11 +232,10 @@ def get_weather(self, reload_type=None, title=""): ) # Check if no city is added - added_cities = self.settings.get_strv("added-cities") - if len(added_cities) == 0: # Reset city to default if all cities are removed - self.settings.reset("added-cities") - self.settings.reset("selected-city") + if len(settings.added_cities) == 0: # Reset city to default if all cities are removed + settings.reset("added-cities") + settings.reset("selected-city") child = self.main_stack.get_child_by_name("main_grid") if child is not None: @@ -349,7 +348,7 @@ def _refresh_weather(self, widget): # ============= Dynamic Background methods ============== def _use_dynamic_bg(self, weather_code: int = 0, is_day: int = 1): - if self.settings.get_boolean("use-gradient-bg"): + if settings.is_using_dynamic_bg: dont_delete_classes = ["backgrounds", "csd"] for cl in self.get_css_classes(): if cl not in dont_delete_classes: diff --git a/src/units.py b/src/units.py deleted file mode 100644 index 4da31da..0000000 --- a/src/units.py +++ /dev/null @@ -1,5 +0,0 @@ -from gi.repository import Gio - -def get_measurement_type(): - settings = Gio.Settings.new("io.github.amit9838.mousam") - return settings.get_string("measure-type") diff --git a/src/utils.py b/src/utils.py index 85f3e0d..3965c3b 100644 --- a/src/utils.py +++ b/src/utils.py @@ -3,13 +3,13 @@ import json from datetime import datetime, timedelta, timezone import time -from gi.repository import Adw, Gio +from gi.repository import Adw +from .config import settings current_weather_data = None air_pollution_data = None forecast_weather_data = None epoch_offset = None -global_settings = Gio.Settings.new("io.github.amit9838.mousam") TIMEOUT = 5 domains = { @@ -53,19 +53,12 @@ def check_internet_connection(): def get_selected_city_coords(): - settings = Gio.Settings.new("io.github.amit9838.mousam") - - selected_city = int(str(settings.get_value("selected-city"))) - added_cities = list(settings.get_value("added-cities")) + selected_city = int(str(settings.selected_city)) + added_cities = list(settings.added_cities) city_loc = added_cities[selected_city].split(",") return city_loc[-2], city_loc[-1] # latitude,longitude -def is_dynamic_bg_enabled(): - global global_settings - return global_settings.get_boolean("use-gradient-bg") - - def create_toast(text, priority=0): toast = Adw.Toast.new(text) toast.set_priority(Adw.ToastPriority(priority)) @@ -80,8 +73,7 @@ def convert_to_local_time(timestamp, timezone_stamp): def get_cords(): - global global_settings - selected_city_ = global_settings.get_string("selected-city") + selected_city_ = settings.selected_city return [float(x) for x in selected_city_.split(",")] diff --git a/src/weatherData.py b/src/weatherData.py index e3b7a25..cf0b559 100644 --- a/src/weatherData.py +++ b/src/weatherData.py @@ -1,9 +1,9 @@ import time import gi -from gi.repository import Gio from .backendWeather import Weather from .backendAirPollution import AirPollution +from .config import settings from .Models import * from .utils import get_cords from gettext import gettext as _, pgettext as C_ @@ -158,11 +158,9 @@ def classify_wind_speed_level(wind_speed): def transform_visibility_data(unit, data): - settings = Gio.Settings(schema_id="io.github.amit9838.mousam") - measurement_type = settings.get_string("measure-type") dist_unit = _("km") dist = data / 1000 - if measurement_type == "imperial": + if settings.unit == "imperial": dist_unit = _("miles") dist = data / 1609.34 diff --git a/src/windowLocations.py b/src/windowLocations.py index 76b8f3d..db52b1f 100644 --- a/src/windowLocations.py +++ b/src/windowLocations.py @@ -2,30 +2,31 @@ import time import gi -gi.require_version('Gtk', '4.0') -gi.require_version('Adw', '1') -from gi.repository import Gtk, Adw, GLib +gi.require_version("Gtk", "4.0") +gi.require_version("Adw", "1") +from gi.repository import Gtk, Adw from .utils import create_toast from .backendFindCity import find_city +from .config import settings global updated_at updated_at = time.time() + class WeatherLocations(Adw.PreferencesWindow): - def __init__(self, application, **kwargs): + def __init__(self, application, **kwargs): super().__init__(**kwargs) self.application = application self.set_title(_("Locations")) self.set_transient_for(application) self.set_default_size(600, 500) - # Settings - global selected_city,added_cities,cities - self.settings = application.settings - selected_city = self.settings.get_string('selected-city') - added_cities = self.settings.get_strv('added-cities') - cities = [x.split(',')[0] for x in added_cities] + # Settings + global selected_city_item, added_cities_list, cities + selected_city_item = settings.selected_city + added_cities_list = settings.added_cities + cities = [x.split(",")[0] for x in added_cities_list] # ============= Location Page ============= location_page = Adw.PreferencesPage() @@ -37,7 +38,9 @@ def __init__(self, application, **kwargs): # Add location button with plus icon add_loc_btn = Gtk.Button() - add_loc_btn_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER,spacing=4) + add_loc_btn_box = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, valign=Gtk.Align.CENTER, spacing=4 + ) label = Gtk.Label(label=_("Add")) add_loc_btn_box.append(label) @@ -45,84 +48,94 @@ def __init__(self, application, **kwargs): add_icon.set_pixel_size(14) add_loc_btn_box.append(add_icon) add_loc_btn.set_child(add_loc_btn_box) - add_loc_btn.connect('clicked',self._add_location_dialog) + add_loc_btn.connect("clicked", self._add_location_dialog) self.location_grp.set_header_suffix(add_loc_btn) self.location_rows = [] - self._create_cities_list(added_cities) + self._create_cities_list(added_cities_list) # =========== Location page methods ============= - def _create_cities_list(self,data): - if len(self.location_rows)>0: + def _create_cities_list(self, data): + if len(self.location_rows) > 0: for action_row in self.location_rows: self.location_grp.remove(action_row) self.location_rows.clear() - for city in added_cities: + for city in added_cities_list: button = Gtk.Button() button.set_icon_name("edit-delete-symbolic") - button.set_css_classes(['circular']) + button.set_css_classes(["circular"]) button.set_tooltip_text(_("Remove location")) button.set_has_frame(False) - # Add ckeck icon if city is selected - box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER) - selected_city_index = list(map(lambda city: selected_city in city,added_cities)).index(True) - if added_cities[selected_city_index] == city: + # Add ckeck icon if city is selected + box = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, valign=Gtk.Align.CENTER + ) + selected_city_index = list( + map(lambda city: selected_city_item in city, added_cities_list) + ).index(True) + if added_cities_list[selected_city_index] == city: check_icon = Gtk.Image() - check_icon.set_from_icon_name("emblem-ok-symbolic") # Set the icon name and size + check_icon.set_from_icon_name( + "emblem-ok-symbolic" + ) # Set the icon name and size check_icon.set_pixel_size(18) check_icon.set_margin_end(15) box.append(check_icon) box.append(button) # Location Row - location_row = Adw.ActionRow.new() + location_row = Adw.ActionRow.new() location_row.set_activatable(True) location_row.set_title(f"{city.split(',')[0]},{city.split(',')[1]}") location_row.set_subtitle(f"{city.split(',')[-2]},{city.split(',')[-1]}") location_row.add_suffix(box) - + # Location row signal location_row.connect("activated", self.switch_location) self.location_rows.append(location_row) self.location_grp.add(location_row) - button.connect("clicked", self._remove_city,location_row) + button.connect("clicked", self._remove_city, location_row) # ========== Switch Location ============ - def switch_location(self,widget): - global selected_city + def switch_location(self, widget): + global selected_city_item title = widget.get_title() select_cord = f"{widget.get_subtitle()}" - + if len(select_cord.split(",")) < 2: return # Switch if location is not already selected - if selected_city != select_cord: - selected_city = select_cord - self.settings.set_value("selected-city",GLib.Variant("s",selected_city)) - self._create_cities_list(added_cities) - global updated_at + if selected_city_item != select_cord: + selected_city_item = select_cord + settings.selected_city = selected_city_item + self._create_cities_list(added_cities_list) + global updated_at # Ignore refreshing weather within 5 second if time.time() - updated_at < 2: updated_at = time.time() - self.add_toast(create_toast(_("Switch city within 2 seconds is ignored!"),1)) + self.add_toast( + create_toast(_("Switch city within 2 seconds is ignored!"), 1) + ) else: updated_at = time.time() - self.add_toast(create_toast(_("Selected - {}").format(title),1)) - thread = threading.Thread(target=self.application._load_weather_data,name="load_data") + self.add_toast(create_toast(_("Selected - {}").format(title), 1)) + thread = threading.Thread( + target=self.application._load_weather_data, name="load_data" + ) thread.start() # ========== Add Location =========== - def _add_location_dialog(self,application): + def _add_location_dialog(self, application): # Create dialog to search and add location self._dialog = Adw.PreferencesWindow() self._dialog.set_search_enabled(False) - self._dialog.set_title(title=_('Add New Location')) + self._dialog.set_title(title=_("Add New Location")) self._dialog.set_transient_for(self) self._dialog.set_default_size(300, 500) @@ -131,29 +144,36 @@ def _add_location_dialog(self,application): self._dialog.group = Adw.PreferencesGroup() self._dialog.page.add(self._dialog.group) - + # Create search box - search_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER, spacing = 6, margin_bottom=10,) + search_box = Gtk.Box( + orientation=Gtk.Orientation.HORIZONTAL, + valign=Gtk.Align.CENTER, + spacing=6, + margin_bottom=10, + ) search_box.set_hexpand(True) self._dialog.group.add(search_box) self.search_entry = Gtk.Entry() self.search_entry.connect("activate", self._on_find_city_clicked) - self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition(1),'edit-clear-symbolic') + self.search_entry.set_icon_from_icon_name( + Gtk.EntryIconPosition(1), "edit-clear-symbolic" + ) self.search_entry.set_placeholder_text(_("Search for a city")) self.search_entry.set_hexpand(True) - self.search_entry.connect('icon-press',self._clear_search_box) + self.search_entry.connect("icon-press", self._clear_search_box) search_box.append(self.search_entry) - # Create search button + # Create search button button = Gtk.Button(label=_("Search")) - button.set_icon_name('system-search-symbolic') + button.set_icon_name("system-search-symbolic") button.set_tooltip_text(_("Search")) search_box.append(button) self._dialog.serach_res_grp = Adw.PreferencesGroup() self._dialog.serach_res_grp.set_hexpand(True) - self._blank_search_page('start') + self._blank_search_page("start") self._dialog.group.add(self._dialog.serach_res_grp) button.connect("clicked", self._on_find_city_clicked) @@ -161,38 +181,38 @@ def _add_location_dialog(self,application): self._dialog.show() # ============ Clear Search results ============ - def _clear_search_box(self,widget,pos): + def _clear_search_box(self, widget, pos): self.search_entry.set_text("") # =========== Click on find city =========== - def _on_find_city_clicked(self,widget): + def _on_find_city_clicked(self, widget): self._find_city(widget) # =========== Find city =========== - def _find_city(self,widget): + def _find_city(self, widget): text = self.search_entry.get_text() - + # Matched city from api - city_data = find_city(text,5) + city_data = find_city(text, 5) self._dialog.serach_res_grp.remove(self.search_page_start) - if len(self._dialog.search_results)>0: + if len(self._dialog.search_results) > 0: for action_row in self._dialog.search_results: self._dialog.serach_res_grp.remove(action_row) self._dialog.search_results.clear() - + # Plot search results if found if city_data: for loc in city_data: - res_row = Adw.ActionRow.new() + res_row = Adw.ActionRow.new() res_row.set_activatable(True) - title_arr = [loc.name,loc.state,loc.country] + title_arr = [loc.name, loc.state, loc.country] title_arr = [x for x in title_arr if x is not None] title = ",".join(title_arr) res_row.set_title(title) # Skip plotting the location in the search results if it has invalid cords - if loc.latitude is None or loc.longitude is None: + if loc.latitude is None or loc.longitude is None: continue if loc.latitude == "" or loc.longitude == "": continue @@ -201,82 +221,89 @@ def _find_city(self,widget): res_row.connect("activated", self._add_city) self._dialog.search_results.append(res_row) self._dialog.serach_res_grp.add(res_row) - + # If no search result is found else: self._blank_search_page(status="no_res") - # =========== Add City on selection =========== - def _add_city(self,widget): + def _add_city(self, widget): # get title,subtitle from selected item in search result title = widget.get_title() - title_arr = title.split(',') + title_arr = title.split(",") modified_title = title_arr[0] - if len(title_arr)>2: + if len(title_arr) > 2: modified_title = f"{title_arr[0]},{title_arr[2]}" - elif len(title_arr)>1: + elif len(title_arr) > 1: modified_title = f"{title_arr[0]},{title_arr[1]}" loc_city = f"{modified_title},{widget.get_subtitle()}" - # Add city to db if it is not already added - if loc_city not in added_cities: - added_cities.append(loc_city) - self.settings.set_value("added-cities",GLib.Variant("as",added_cities)) - self._create_cities_list(added_cities) + # Add city to db if it is not already added + if loc_city not in added_cities_list: + added_cities_list.append(loc_city) + settings.added_cities = added_cities_list + self._create_cities_list(added_cities_list) # self.application.refresh_main_ui() - self._dialog.add_toast(create_toast(_("Added - {0}").format(title),1)) + self._dialog.add_toast(create_toast(_("Added - {0}").format(title), 1)) else: - self._dialog.add_toast(create_toast(_("Location already added!"),1)) + self._dialog.add_toast(create_toast(_("Location already added!"), 1)) # ========== Remove City =========== - def _remove_city(self,btn,widget): - global selected_city - city = f"{widget.get_title()},{widget.get_subtitle()}" - - # Don't delete city if only one item is present in the list - if len(added_cities)==1: - self.add_toast(create_toast(_("Add more locations to delete!"),1)) - return - - selected_city_index = list(map(lambda x: selected_city in x, added_cities)).index(True) - s_city = added_cities[selected_city_index] - added_cities.remove(city) - - # If selected city is removed then select first city in the list - if widget.get_subtitle() == selected_city: - first_city = added_cities[0].split(",") - selected_city = f"{first_city[-2]},{first_city[-1]}" - self.settings.set_value("selected-city",GLib.Variant("s",selected_city)) - thread = threading.Thread(target=self.application._load_weather_data,name="load_data") - thread.start() + def _remove_city(self, btn, widget): + global selected_city_item + city = f"{widget.get_title()},{widget.get_subtitle()}" - self.settings.set_value("added-cities",GLib.Variant("as",added_cities)) - self._create_cities_list(added_cities) - if s_city == city: # fetch weather only if selected_city was removed - pass - # self.application.refresh_weather(self.application) - else: - pass - # self.application.refresh_main_ui() - self.add_toast(create_toast(_("Deleted - {0}".format(widget.get_title())),1)) + # Don't delete city if only one item is present in the list + if len(added_cities_list) == 1: + self.add_toast(create_toast(_("Add more locations to delete!"), 1)) + return + + selected_city_index = list( + map(lambda x: selected_city_item in x, added_cities_list) + ).index(True) + s_city = added_cities_list[selected_city_index] + added_cities_list.remove(city) + + # If selected city is removed then select first city in the list + if widget.get_subtitle() == selected_city_item: + first_city = added_cities_list[0].split(",") + selected_city_item = f"{first_city[-2]},{first_city[-1]}" + settings.selected_city = selected_city_item + thread = threading.Thread( + target=self.application._load_weather_data, name="load_data" + ) + thread.start() + + settings.added_cities = added_cities_list + self._create_cities_list(added_cities_list) + if s_city == city: # fetch weather only if selected_city was removed + pass + # self.application.refresh_weather(self.application) + else: + pass + # self.application.refresh_main_ui() + self.add_toast(create_toast(_("Deleted - {0}".format(widget.get_title())), 1)) - def _blank_search_page(self,status): + def _blank_search_page(self, status): text = _("Press enter to search") - icon ="system-search-symbolic" - if status == 'no_res': + icon = "system-search-symbolic" + if status == "no_res": text = _("No results found!") - icon ="system-search-symbolic" - - self.search_page_start = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,halign=Gtk.Align.CENTER, spacing = 6, margin_top=60,) + icon = "system-search-symbolic" + + self.search_page_start = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, + halign=Gtk.Align.CENTER, + spacing=6, + margin_top=60, + ) search_icon = Gtk.Image.new_from_icon_name(icon) search_icon.set_pixel_size(48) search_icon.set_margin_top(10) search_icon.set_css_classes(["light-4"]) - search_page_start_text = Gtk.Label(label = text) + search_page_start_text = Gtk.Label(label=text) search_page_start_text.set_css_classes(["text-3", "bold-3", "light-5"]) self.search_page_start.append(search_icon) self.search_page_start.append(search_page_start_text) self._dialog.serach_res_grp.add(self.search_page_start) - diff --git a/src/windowPreferences.py b/src/windowPreferences.py index c6b3826..9dc6616 100644 --- a/src/windowPreferences.py +++ b/src/windowPreferences.py @@ -6,8 +6,8 @@ gi.require_version('Adw', '1') from gi.repository import Gtk, Adw,GLib -from .units import get_measurement_type from .utils import create_toast +from .config import settings global updated_at updated_at = time.time() @@ -20,15 +20,6 @@ def __init__(self, application, **kwargs): self.set_transient_for(application) self.set_default_size(600, 500) - global selected_city,added_cities,cities,use_personal_api,isValid_personal_api,personal_api_key,measurement_type - self.settings = application.settings - selected_city = self.settings.get_string('selected-city') - added_cities = list(self.settings.get_strv('added-cities')) - should_launch_maximized = self.settings.get_boolean('launch-maximized') - cities = [x.split(',')[0] for x in added_cities] - measurement_type = get_measurement_type() - - # =============== Appearance Page =============== appearance_page = Adw.PreferencesPage() appearance_page.set_title(_("Appearance")) @@ -46,7 +37,7 @@ def __init__(self, application, **kwargs): self.g_switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER) self.gradient_switch = Gtk.Switch() - self.gradient_switch.set_active(self.settings.get_boolean('use-gradient-bg')) + self.gradient_switch.set_active(settings.is_using_dynamic_bg) self.gradient_switch.connect("state-set",self._use_gradient_bg) self.g_switch_box.append(self.gradient_switch) gradient_row.add_suffix(self.g_switch_box) @@ -60,7 +51,7 @@ def __init__(self, application, **kwargs): self.g_switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER) self.launch_max_switch = Gtk.Switch() - self.launch_max_switch.set_active(should_launch_maximized) + self.launch_max_switch.set_active(settings.should_launch_maximized) self.launch_max_switch.connect("state-set",self._on_click_launch_maximixed) self.g_switch_box.append(self.launch_max_switch) launch_maximized.add_suffix(self.g_switch_box) @@ -90,7 +81,7 @@ def __init__(self, application, **kwargs): self.imperial_unit.set_activatable_widget(self.imperial_check_btn) self.imperial_unit.connect("activated", self._change_unit,'imperial') self.measurement_group.add(self.imperial_unit) - GLib.idle_add(self.metric_unit.activate) if measurement_type == 'metric' else GLib.idle_add(self.imperial_unit.activate) + GLib.idle_add(self.metric_unit.activate) if settings.unit == 'metric' else GLib.idle_add(self.imperial_unit.activate) self.prec_unit_group = Adw.PreferencesGroup.new() self.prec_unit_group.set_margin_top(20) @@ -102,7 +93,7 @@ def __init__(self, application, **kwargs): self.prec_unit_switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,valign=Gtk.Align.CENTER) self.prec_unit.set_activatable(True) self.use_inch_switch = Gtk.Switch() - self.use_inch_switch.set_active(self.settings.get_boolean('use-inch-for-prec')) + self.use_inch_switch.set_active(settings.is_using_inch_for_prec) self.use_inch_switch.connect("state-set",self._use_inch_for_precipation) self.prec_unit_switch_box.append(self.use_inch_switch) self.prec_unit.add_suffix(self.prec_unit_switch_box) @@ -110,16 +101,14 @@ def __init__(self, application, **kwargs): # =============== Appearance Methods =============== def _use_gradient_bg(self,widget,state): - self.settings.set_value("use-gradient-bg",GLib.Variant("b",state)) + settings.is_using_dynamic_bg = state def _on_click_launch_maximixed(self,widget,state): - self.settings.set_value("launch-maximized",GLib.Variant("b",state)) + settings.should_launch_maximized = state def _change_unit(self,widget,value): - global measurement_type - if measurement_type != value: - self.settings.set_value("measure-type",GLib.Variant("s",value)) - measurement_type = get_measurement_type() + if settings.unit != value: + settings.unit = value # Ignore refreshing weather within 5 second global updated_at @@ -134,4 +123,4 @@ def _change_unit(self,widget,value): thread.start() def _use_inch_for_precipation(self,widget,state): - self.settings.set_value("use-inch-for-prec",GLib.Variant("b",state)) + settings.is_using_inch_for_prec = state