Added DateWidget

This commit is contained in:
tr1x_em committed 2025-08-03 16:53:34 +05:30
1 parent b7cbeac6d4
commit 2e9c647442
3 files changed
+602 -1

No files matched your search

+41
View File
@@ -71,3 +71,44 @@ menu > menuitem:hover {
menu > menuitem:hover > label {
color: var(--shadow);
}
#day label,
#month label{
color: var(--foreground);
padding: 5px;
margin-top: 10px;
font-size: 30px;
color: alpha(#fff, 0.7);
font-family: "SF Pro Rounded";
font-weight: 600;
}
#date label{
color: alpha(#fff, 0.7);
margin-top: -10px;
font-size: 90px;
font-weight: 600;
font-family: "SF Pro Rounded";
}
#day label{
color: alpha(#FF0000, 0.7);
}
#date{
}
#box-widget{
background-color: alpha(#232323, 1);
margin-top: 20px;
min-width: 170px;
min-height: 170px;
border-radius: 16px;
}
+6 -1
View File
@@ -1,3 +1,4 @@
from fabric.core import widgets
import setproctitle
from loguru import logger
@@ -7,6 +8,7 @@ from fabric.utils import get_relative_path, monitor_file
from modules.dock import Dock
from modules.launcher.main import Launcher
from modules.notification.notification import ModusNoti
from modules.widget import Deskwidgets
from modules.osd import OSD
# from modules.corners import Corners
@@ -41,6 +43,7 @@ if __name__ == "__main__":
panel.launcher = launcher
osd = OSD()
widgets = Deskwidgets()
# Set corners visibility based on config
# corners_visible = config.get("corners_visible", True)
# corners.set_visible(corners_visible)
@@ -50,7 +53,9 @@ if __name__ == "__main__":
_ = css_file.connect("changed", lambda *_: set_css())
# Make sure corners is added to the app
app = Application(f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd)
app = Application(
f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd, widgets
)
def set_css():
app.set_stylesheet_from_file(
+555
View File
@@ -0,0 +1,555 @@
import psutil
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.overlay import Overlay
from fabric.widgets.datetime import DateTime
from fabric.widgets.circularprogressbar import CircularProgressBar
from widgets.wayland import WaylandWindow as Window
from fabric.utils import invoke_repeater
import requests
import urllib.parse
import datetime
from gi.repository import GLib
from concurrent.futures import ThreadPoolExecutor
import time
import config.data as data
from config.data import load_config
import subprocess
executor = ThreadPoolExecutor(max_workers=4)
config = load_config()
def margin():
return (
data.DOCK_ICON_SIZE + 10
if not data.DOCK_ALWAYS_OCCLUDED and data.DOCK_ENABLED
else 0
)
def get_location():
"""Fetch location from config file or IP API asynchronously."""
for attempt in range(5):
try:
response = requests.get("https://ipinfo.io/json", timeout=3)
if response.status_code == 200:
return response.json().get("city", "").replace(" ", "")
except requests.RequestException as e:
print(f"Error getting location: {e}")
if attempt < 4:
time.sleep(10)
else:
return ""
def get_location_async(callback):
"""Fetch location asynchronously to prevent UI freeze."""
executor.submit(lambda: GLib.idle_add(callback, get_location()))
def get_weather(callback):
"""Fetch weather data asynchronously and update UI."""
def fetch_weather():
location = get_location()
if not location:
return GLib.idle_add(callback, None)
encoded_location = urllib.parse.quote(location)
url = f"https://wttr.in/{encoded_location}?format=j1"
urlemoji = f"https://wttr.in/{encoded_location}?format=%c"
for attempt in range(5):
try:
response = requests.get(urlemoji, timeout=3)
responseinfo = requests.get(url, timeout=3).json()
if response.status_code == 200:
temp_unit = "C"
temp = (
responseinfo["current_condition"][0][f"temp_{temp_unit}"] + "°"
)
feels_like = (
responseinfo["current_condition"][0][f"FeelsLike{temp_unit}"]
+ "°"
)
condition = responseinfo["current_condition"][0]["weatherDesc"][0][
"value"
]
location = responseinfo["nearest_area"][0]["areaName"][0]["value"]
emoji = response.text.strip()
update_time = datetime.datetime.now().strftime("%I:%M:%S %p")
GLib.idle_add(
callback,
[emoji, temp, condition, feels_like, location, update_time],
)
return
except requests.RequestException as e:
print(f"Error fetching weather (attempt {attempt + 1}): {e}")
if attempt < 4:
time.sleep(10)
else:
GLib.idle_add(callback, None)
return
executor.submit(fetch_weather)
def update_weather(widget):
def fetch_and_update():
get_weather(lambda weather_info: update_widget(widget, weather_info))
return True
GLib.timeout_add_seconds(600, fetch_and_update)
fetch_and_update()
def update_widget(widget, weather_info):
if weather_info:
widget.weatherinfo = weather_info
widget.update_labels(weather_info)
class Sysinfo(Box):
@staticmethod
def bake_progress_bar(name: str = "progress-bar", size: int = 45, **kwargs):
return CircularProgressBar(
name=name,
start_angle=180,
end_angle=540,
min_value=0,
max_value=100,
size=size,
**kwargs,
)
@staticmethod
def bake_progress_icon(**kwargs):
return Label(**kwargs).build().add_style_class("progress-icon").unwrap()
def __init__(self, **kwargs):
super().__init__(
layer="bottom",
title="sysinfo",
name="sysinfo",
visible=False,
all_visible=False,
**kwargs,
)
self.cpu_progress = self.bake_progress_bar()
self.ram_progress = self.bake_progress_bar()
self.bat_circular = self.bake_progress_bar().build().set_value(42).unwrap()
self.progress_container = Box(
name="progress-bar-container",
spacing=12,
children=[
Box(
children=[
Overlay(
child=self.cpu_progress,
tooltip_text="",
overlays=[
self.bake_progress_icon(
label="",
name="progress-icon-cpu",
)
],
),
],
),
Box(
children=[
Overlay(
child=self.ram_progress,
tooltip_text="",
overlays=[
self.bake_progress_icon(
name="progress-icon-ram",
label="󰘚",
)
],
)
]
),
Box(
children=[
Overlay(
child=self.bat_circular,
tooltip_text="",
overlays=[
self.bake_progress_icon(
label="󱊣",
name="progress-icon-bat",
)
],
),
],
),
],
)
self.update_status()
invoke_repeater(1000, self.update_status)
self.add(
Box(
name="progress-bar-container-main",
orientation="v",
spacing=24,
children=[self.progress_container],
),
)
self.show_all()
def update_status(self):
"""Update system info asynchronously to prevent UI lag."""
def update():
cpu = psutil.cpu_percent()
ram = psutil.virtual_memory().percent
battery = (
psutil.sensors_battery().percent if psutil.sensors_battery() else 80
)
GLib.idle_add(self.cpu_progress.set_value, cpu)
GLib.idle_add(self.ram_progress.set_value, ram)
GLib.idle_add(self.bat_circular.set_value, battery)
GLib.idle_add(self.cpu_progress.set_tooltip_text, f"{str(round(cpu))}%")
GLib.idle_add(self.ram_progress.set_tooltip_text, f"{str(round(ram))}%")
GLib.idle_add(self.bat_circular.set_tooltip_text, f"{str(round(battery))}%")
executor.submit(update)
return True
class WeatherWidget(Box):
def __init__(self, **kwargs):
super().__init__(
layer="bottom",
name="weather-widget",
anchor="center",
h_align="center",
v_align="center",
h_expand=True,
visible=False,
v_expand=True,
**kwargs,
)
self.weatherinfo = None
self.header = Box(
name="header1",
orientation="h",
h_expand=True,
children=[
Box(
orientation="v",
children=[
Label(label="", h_align="start", name="weather-location"),
Label(label="", h_align="start", name="weather-update-time"),
Label(label="", h_align="start", name="weather-condition"),
],
),
],
)
self.temp = Box(
name="header2",
orientation="h",
h_align="center",
children=[
Box(
orientation="v",
children=[
Label(label="", name="weather-current-temperature"),
Label(label="", name="weather-feels-like-temperature"),
],
),
],
)
self.header_right = Box(
name="header3",
orientation="h",
children=[
Box(
orientation="v",
children=[
Label(label="", name="weather-emoji"),
],
),
],
)
self.add(
Box(
name="window-inner",
orientation="h",
h_align="center",
v_align="center",
children=[self.header, self.temp, self.header_right],
),
)
self.set_visible(False)
update_weather(self)
def update_labels(self, weather_info):
if not self.weatherinfo:
return
# Unpack weather info into variables for better readability
emoji, temp, condition, feels_like, location, update_time = self.weatherinfo
# Store references to deeply nested children to avoid repeated lookups
header_left = self.header.children[0].children
temp_labels = self.temp.children[0].children
header_right = self.header_right.children[0].children
# Update labels efficiently
self.set_visible(True)
header_left[0].set_label(location)
header_left[1].set_label(f"Updated {update_time}")
header_left[2].set_label(condition)
temp_labels[0].set_label(temp)
temp_labels[1].set_label(f"Feels like {feels_like}")
header_right[0].set_label(emoji)
def fetch_quote(callback):
"""Fetch quotes asynchronously."""
def fetch():
quotes_type = "stoic"
url = (
"https://stoic-quotes.com/api/quote"
if quotes_type == "stoic"
else "https://zenquotes.io/api/random"
)
for attempt in range(5):
try:
response = requests.get(url, timeout=3)
response.raise_for_status()
respdata = response.json()
quote = (
f"{respdata[0]['q']} - {respdata[0]['a']}"
if quotes_type == "zen"
else f"{respdata['text']} - {respdata['author']}"
)
break
except requests.RequestException as e:
print(f"Error fetching quote: {e}")
if attempt < 4:
time.sleep(10)
else:
try:
result = subprocess.run(
["hyprctl", "splash"],
capture_output=True,
text=True,
check=True,
)
quote = result.stdout.strip() + " - Team Hyprland"
except subprocess.CalledProcessError as e:
print(f"Error fetching quote from hyprctl: {e}")
quote = "I learn from the mistakes of people who take my advice - Trix"
GLib.idle_add(callback, quote)
executor.submit(fetch)
def fetch_quote_async(callback):
GLib.idle_add(lambda: fetch_quote(callback))
class QuoteWidget(Label):
def __init__(self, **kwargs):
super().__init__(
name="quote",
label="",
anchor="center",
h_align="center",
v_align="center",
h_expand=True,
justification="center",
v_expand=True,
visible=False,
)
fetch_quote_async(self.update_label)
def update_label(self, quote):
"""Update quote asynchronously."""
max_width = 150 # Set the maximum width for the quote
if len(quote) > max_width:
words = quote.split()
line1, line2 = "", ""
for word in words:
if len(line1) + len(word) + 1 <= max_width:
line1 += word + " "
else:
line2 += word + " "
quote = line1.strip() + "\n" + line2.strip()
self.set_label(quote)
self.set_visible(True)
class ActivationMainText(Label):
def __init__(self, **kwargs):
super().__init__(
name="activation-main-text",
label="",
anchor="bottom right",
justification="left",
v_align="start",
h_align="start",
h_expand=True,
v_expand=True,
visible=False,
)
self.set_label("Activate Linux")
class ActivationSubText(Label):
def __init__(self, **kwargs):
super().__init__(
name="activation-sub-text",
label="",
anchor="bottom right",
justification="left",
v_align="start",
h_align="start",
h_expand=True,
v_expand=True,
visible=False,
)
self.set_label("Go to Settings to activate Linux")
def create_widgets(config):
widgets = []
if config.get("widgets_displaytype_visible", True):
if config.get("widgets_clock_visible", True):
widgets.append(
DateTime(formatters=["%A, %d %B"], interval=10000, name="date")
)
if config.get("widgets_date_visible", True):
widgets.append(DateTime(formatters=["%I:%M %p"], name="clock"))
if config.get("widgets_quote_visible", True):
widgets.append(QuoteWidget())
if config.get("widgets_weatherwid_visible", True):
widgets.append(WeatherWidget())
else:
widgets.append(DateTime(formatters=["%I:%M %p"], name="clock"))
widgets.append(DateTime(formatters=["%A. %d %B"], interval=10000, name="date"))
return widgets
# if data.DESKTOP_WIDGETS:
# if config.get("widgets_displaytype_visible", True):
class Date(Box):
def __init__(self, **kwargs):
super().__init__(
name="date-widget",
h_expand=True,
v_expand=True,
justification="center",
h_align="center",
v_align="start",
orientation="v",
**kwargs,
)
self.top = Box(orientation="h", name="date-top", h_expand=True)
self.dateone = DateTime(formatters=["%a"], interval=10000, name="day")
self.datetwo = DateTime(formatters=["%b"], interval=10000, name="month")
self.datethree = DateTime(formatters=["%-d"], interval=10000, name="date")
self.top.add(self.dateone)
self.top.add(self.datetwo)
self.add(self.top)
self.add(self.datethree)
class Deskwidgets(Window):
def __init__(self, **kwargs):
config = load_config()
super().__init__(
name="desktop",
layer="bottom",
title="desktop-widgets",
exclusivity="none",
child=[
Window(
anchor="top left",
layer="bottom",
child=[
Box(
orientation="v",
name="box-widget",
v_expand=True,
v_align="center",
h_align="center",
children=[Date()],
)
],
)
],
all_visible=True,
**kwargs,
)
# else:
#
# class Deskwidgets(Window):
# def __init__(self, **kwargs):
# config = load_config()
# super().__init__(name="desktop", **kwargs)
# desktop_widget = Window(
# layer="bottom",
# anchor="bottom left",
# v_align="start",
# h_align="start",
# h_expand=True,
# v_expand=True,
# justification="left",
# exclusivity="none",
# child=Box(
# orientation="v",
# children=create_widgets(config),
# ),
# all_visible=True,
# )
# if config.get("widgets_activation_visible", True):
# activationnag = Window(
# name="activation",
# anchor="bottom right",
# layer="top",
# justification="left",
# v_align="start",
# h_align="start",
# h_expand=True,
# v_expand=True,
# child=Box(
# orientation="v",
# children=[
# ActivationMainText(),
# ActivationSubText(),
# ],
# ),
# all_visible=True,
# )
# else:
# activationnag = None
#
# else:
#
# class Deskwidgets(Window):
# def __init__(self, **kwargs):
# super().__init__(name="desktop", **kwargs)
# self.set_visible(False)