Skip to content

Commit

Permalink
feat(widget/clock): add calendar settings and update callbacks
Browse files Browse the repository at this point in the history
Introduced a new calendar configuration to the clock widget, allowing customization of blur effects, corner rounding, border color, alignment, direction, and distance. Updated the callbacks to include 'toggle_calendar' functionality for enhanced interactivity.
  • Loading branch information
amnweb committed Jan 24, 2025
1 parent 57d6c63 commit 0210573
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 9 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 63 additions & 1 deletion docs/widgets/(Widget)-Clock.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
| `locale` | string | `""` | The locale to use for the clock. If not specified, it defaults to an empty string. |
| `update_interval` | integer | `1000` | The interval in milliseconds to update the clock. Must be between 0 and 60000. |
| `timezones` | list | `[]` | A list of timezones to cycle through. Each timezone should be a valid timezone string. |
| `callbacks` | dict | `{'on_left': 'toggle_label', 'on_middle': 'do_nothing', 'on_right': 'next_timezone'}` | Callbacks for mouse events on the clock widget. |
| `calendar` | dict | `{'blur': True, 'round_corners': True, 'round_corners_type': 'normal', 'border_color': 'System', 'alignment': 'right', 'direction': 'down', 'distance': 6}` | Calendar settings for the widget. |
| `callbacks` | dict | `{'on_left': 'toggle_calendar', 'on_middle': 'next_timezone', 'on_right': 'toggle_label'}` | Callbacks for mouse events on the clock widget. |
| `animation` | dict | `{'enabled': True, 'type': 'fadeInOut', 'duration': 200}` | Animation settings for the widget. |
| `container_padding` | dict | `{'top': 0, 'left': 0, 'bottom': 0, 'right': 0}` | Explicitly set padding inside widget container. |

Expand All @@ -23,6 +24,14 @@ clock:
locale: ""
update_interval: 1000
timezones: []
calendar:
blur: True
round_corners: True
round_corners_type: 'normal'
border_color: 'System'
alignment: 'center'
direction: 'down'
distance: 6
callbacks:
on_left: "toggle_label"
on_middle: "do_nothing"
Expand All @@ -37,6 +46,14 @@ clock:
- **tooltip:** Whether to show the tooltip on hover.
- **update_interval:** The interval in milliseconds to update the clock. Must be between 0 and 60000.
- **timezones:** A list of timezones to cycle through. If value is empty YASB will looking up time zone info from registry
- **calendar:** A dictionary specifying the calendar settings for the widget. It contains the following keys:
- **blur:** Enable blur effect for the calendar.
- **round_corners:** Enable round corners for the calendar (this option is not supported on Windows 10).
- **round_corners_type:** Set the type of round corners for the calendar (normal, small) (this option is not supported on Windows 10).
- **border_color:** Set the border color for the calendar (this option is not supported on Windows 10).
- **alignment:** Set the alignment of the calendar (left, right).
- **direction:** Set the direction of the calendar (up, down).
- **distance:** Set the distance of the calendar from the widget.
- **callbacks:** A dictionary specifying the callbacks for mouse events. The keys are `on_left`, `on_middle`, and `on_right`, and the values are the names of the callback functions.
- **animation:** A dictionary specifying the animation settings for the widget. It contains three keys: `enabled`, `type`, and `duration`. The `type` can be `fadeInOut` and the `duration` is the animation duration in milliseconds.
- **container_padding**: Explicitly set padding inside widget container. Use this option to set padding inside the widget container. You can set padding for top, left, bottom and right sides of the widget container.
Expand All @@ -58,3 +75,48 @@ Clock format https://docs.python.org/3/library/time.html#time.strftime
}
```

## Example Style for the Calendar

```css
.calendar {
background-color: rgba(17, 17, 27, 0.4);
}
.calendar .calendar-table,
.calendar .calendar-table::item {
background-color: rgba(17, 17, 27, 0);
color: rgba(162, 177, 196, 0.85);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
border: none;
outline: none;
}
.calendar .calendar-table::item:selected {
color: rgb(255, 255, 255);
}
.calendar .day-label {
margin-top: 20px;
}
.calendar .day-label,
.calendar .month-label,
.calendar .date-label {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 16px;
color: #fff;
font-weight: 700;
min-width: 180px;
max-width: 180px;
}
.calendar .month-label {
font-weight: normal;
}
.calendar .date-label {
font-size: 88px;
font-weight: 900;
color: rgb(255, 255, 255);
margin-top: -20px;
}
```

## Preview of the Widget
![GitHub YASB Widget](assets/792254956-fr651bd1-gtdc-8966-e89a5edca704.png)
50 changes: 47 additions & 3 deletions src/core/validation/widgets/yasb/clock.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@
'locale':"",
'tooltip': True,
'timezones': [],
'calendar': {
'blur': True,
'round_corners': True,
'round_corners_type': 'normal',
'border_color': 'System',
'alignment': 'right',
'direction': 'down',
'distance': 6
},
'animation': {
'enabled': True,
'type': 'fadeInOut',
'duration': 200
},
'container_padding': {'top': 0, 'left': 0, 'bottom': 0, 'right': 0},
'callbacks': {
'on_left': 'toggle_label',
'on_middle': 'do_nothing',
'on_right': 'next_timezone'
'on_left': 'toggle_calendar',
'on_middle': 'next_timezone',
'on_right': 'toggle_label'
}
}

Expand Down Expand Up @@ -51,6 +60,41 @@
'required': False
}
},
'calendar': {
'type': 'dict',
'required': False,
'schema': {
'blur': {
'type': 'boolean',
'default': DEFAULTS['calendar']['blur']
},
'round_corners': {
'type': 'boolean',
'default': DEFAULTS['calendar']['round_corners']
},
'round_corners_type': {
'type': 'string',
'default': DEFAULTS['calendar']['round_corners_type']
},
'border_color': {
'type': 'string',
'default': DEFAULTS['calendar']['border_color']
},
'alignment': {
'type': 'string',
'default': DEFAULTS['calendar']['alignment']
},
'direction': {
'type': 'string',
'default': DEFAULTS['calendar']['direction']
},
'distance': {
'type': 'integer',
'default': DEFAULTS['calendar']['distance']
}
},
'default': DEFAULTS['calendar']
},
'animation': {
'type': 'dict',
'required': False,
Expand Down
145 changes: 140 additions & 5 deletions src/core/widgets/yasb/clock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,43 @@
import pytz
from core.widgets.base import BaseWidget
from core.validation.widgets.yasb.clock import VALIDATION_SCHEMA
from PyQt6.QtWidgets import QLabel, QHBoxLayout, QWidget
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QWidget, QCalendarWidget, QSizePolicy, QTableView
from PyQt6.QtCore import Qt, QDate, QPoint, QLocale
from datetime import datetime
from tzlocal import get_localzone_name
from itertools import cycle
from core.utils.widgets.animation_manager import AnimationManager
import locale
from core.utils.utilities import PopupWidget

class CustomCalendar(QCalendarWidget):
def __init__(self, parent=None, timezone=None):
super().__init__(parent)
self.timezone = timezone
self.setGridVisible(False)
self.setVerticalHeaderFormat(QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader)
self.setNavigationBarVisible(False)
self.setCursor(Qt.CursorShape.PointingHandCursor)

format = self.weekdayTextFormat(Qt.DayOfWeek.Monday)
for day in range(Qt.DayOfWeek.Monday.value, Qt.DayOfWeek.Sunday.value + 1):
self.setWeekdayTextFormat(Qt.DayOfWeek(day), format)

table_view = self.findChild(QTableView)
if table_view:
table_view.setProperty('class', 'calendar-table')

if parent and parent._locale:
qt_locale = QLocale(parent._locale)
self.setLocale(qt_locale)

self.update_calendar_display()

def update_calendar_display(self):
if self.timezone:
datetime_now = datetime.now(pytz.timezone(self.timezone))
self.setSelectedDate(QDate(datetime_now.year, datetime_now.month, datetime_now.day))

class ClockWidget(BaseWidget):
validation_schema = VALIDATION_SCHEMA

Expand All @@ -20,20 +49,23 @@ def __init__(
locale: str,
tooltip: bool,
update_interval: int,
calendar: dict[str, str],
timezones: list[str],
animation: dict[str, str],
container_padding: dict[str, int],
callbacks: dict[str, str],
):
super().__init__(update_interval, class_name="clock-widget")
self._locale = locale

self._tooltip = tooltip
self._active_tz = None
self._timezones = cycle(timezones if timezones else [get_localzone_name()])
self._active_datetime_format_str = ''
self._active_datetime_format = None
self._animation = animation
self._label_content = label
self._calendar = calendar
self._padding = container_padding
self._label_alt_content = label_alt

Expand All @@ -53,6 +85,7 @@ def __init__(
self.register_callback("toggle_label", self._toggle_label)
self.register_callback("update_label", self._update_label)
self.register_callback("next_timezone", self._next_timezone)
self.register_callback("toggle_calendar", self._toggle_calendar)

self.callback_left = callbacks['on_left']
self.callback_right = callbacks['on_right']
Expand All @@ -65,6 +98,10 @@ def __init__(
self._update_label()
self.start_timer()

def _toggle_calendar(self):
if self._animation['enabled']:
AnimationManager.animate(self, self._animation['type'], self._animation['duration'])
self.show_calendar()

def _toggle_label(self):
if self._animation['enabled']:
Expand Down Expand Up @@ -114,7 +151,7 @@ def _update_label(self):
widget_index = 0
if self._locale:
org_locale = locale.getlocale(locale.LC_TIME)

for part in label_parts:
part = part.strip()
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
Expand All @@ -135,10 +172,108 @@ def _update_label(self):
active_widgets[widget_index].setText(format_label_content)
widget_index += 1
if self._locale:
locale.setlocale(locale.LC_TIME, org_locale)

locale.setlocale(locale.LC_TIME, org_locale)
def _next_timezone(self):
self._active_tz = next(self._timezones)
if self._tooltip:
self.setToolTip(self._active_tz)
self._update_label()



def update_month_label(self, year, month):
qlocale = QLocale(self._locale) if self._locale else QLocale.system()
new_month = qlocale.monthName(month)
new_day = qlocale.dayName(QDate(year, month, 1).dayOfWeek())

self.day_label.setText(new_day)
self.month_label.setText(new_month)

def update_selected_date(self, date: QDate):
qlocale = QLocale(self._locale) if self._locale else QLocale.system()
self.day_label.setText(qlocale.dayName(date.dayOfWeek()))
self.month_label.setText(qlocale.monthName(date.month()))
self.date_label.setText(date.toString("d"))


def show_calendar(self):
# Create popup widget
self._yasb_calendar = PopupWidget(self, self._calendar['blur'], self._calendar['round_corners'],
self._calendar['round_corners_type'], self._calendar['border_color'])
self._yasb_calendar.setProperty('class', 'calendar')
self._yasb_calendar.setWindowFlag(Qt.WindowType.FramelessWindowHint)
self._yasb_calendar.setWindowFlag(Qt.WindowType.Popup)
self._yasb_calendar.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint)

# Create main layout
layout = QHBoxLayout()
layout.setProperty('class', 'calendar-layout')
self._yasb_calendar.setLayout(layout)

# Left side: Today Date
date_layout = QVBoxLayout()
date_layout.setContentsMargins(0, 0, 0, 0)
date_layout.setSpacing(0)
date_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)

datetime_now = datetime.now(pytz.timezone(self._active_tz))
qlocale = QLocale(self._locale) if self._locale else QLocale.system()

self.day_label = QLabel(qlocale.dayName(QDate(datetime_now.year, datetime_now.month, datetime_now.day).dayOfWeek()))
self.day_label.setProperty('class', 'day-label')
self.day_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
date_layout.addWidget(self.day_label)

self.month_label = QLabel(qlocale.monthName(datetime_now.month))
self.month_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.month_label.setProperty('class', 'month-label')
date_layout.addWidget(self.month_label)

self.date_label = QLabel(str(datetime_now.day))
self.date_label.setProperty('class', 'date-label')
self.date_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
date_layout.addWidget(self.date_label)

layout.addLayout(date_layout)

self.calendar = CustomCalendar(self, self._active_tz)
self.calendar.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)

current_year = QDate.currentDate().year()
min_date = QDate(current_year, 1, 1)
max_date = QDate(current_year, 12, 31)

self.calendar.setMinimumDate(min_date)
self.calendar.setMaximumDate(max_date)
self.calendar.currentPageChanged.connect(self.update_month_label)
self.calendar.clicked.connect(self.update_selected_date)


layout.addWidget(self.calendar)

# Position and show the popup
self._yasb_calendar.adjustSize()
widget_global_pos = self.mapToGlobal(QPoint(0, self.height() + self._calendar['distance']))

if self._calendar['direction'] == 'up':
global_y = self.mapToGlobal(QPoint(0, 0)).y() - self._yasb_calendar.height() - self._calendar['distance']
widget_global_pos = QPoint(self.mapToGlobal(QPoint(0, 0)).x(), global_y)

if self._calendar['alignment'] == 'left':
global_position = widget_global_pos
elif self._calendar['alignment'] == 'right':
global_position = QPoint(
widget_global_pos.x() + self.width() - self._yasb_calendar.width(),
widget_global_pos.y()
)
elif self._calendar['alignment'] == 'center':
global_position = QPoint(
widget_global_pos.x() + (self.width() - self._yasb_calendar.width()) // 2,
widget_global_pos.y()
)
else:
global_position = widget_global_pos

self._yasb_calendar.move(global_position)
self._yasb_calendar.show()

0 comments on commit 0210573

Please sign in to comment.