Skip to content

Commit d6b9ee4

Browse files
authored
Merge pull request #504 from amnweb/refactor/python-3.13-upgrade
refactor(widgets) add support for python 3.14
2 parents 8eadfa1 + 3448983 commit d6b9ee4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+291
-348
lines changed

.github/workflows/windows-dev.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ permissions:
1717
contents: read
1818

1919
env:
20-
PYTHON_VERSION: '3.12'
20+
PYTHON_VERSION: '3.14'
2121

2222
jobs:
2323
build:
@@ -74,7 +74,7 @@ jobs:
7474
.\venv\Scripts\Activate
7575
python -m pip install --upgrade pip
7676
pip install --force --no-cache .
77-
pip install --force --no-cache --upgrade cx_Freeze==7.2.10
77+
pip install --extra-index-url https://test.pypi.org/simple/ cx_Freeze --pre --no-cache
7878
shell: pwsh
7979

8080
- name: Build EXE

.github/workflows/windows.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ permissions:
77
contents: read
88

99
env:
10-
PYTHON_VERSION: '3.12'
10+
PYTHON_VERSION: '3.14'
1111

1212
jobs:
1313
build:
@@ -36,7 +36,7 @@ jobs:
3636
.\venv\Scripts\Activate
3737
python -m pip install --upgrade pip
3838
pip install --force --no-cache .
39-
pip install --force --no-cache --upgrade cx_Freeze==7.2.10
39+
pip install --force --no-cache --upgrade cx_Freeze
4040
shell: pwsh
4141

4242
- name: Build EXE

docs/widgets/(Widget)-Power-Menu.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ power_menu:
8989
margin: 0px;
9090
}
9191
.power-menu-popup .button.hover {
92-
background-color: #191919;
93-
border: 4px solid #191919;
92+
background-color: #1d1d1d;
93+
border: 4px solid #1d1d1d;
9494
}
9595
.power-menu-popup .button .label {
9696
margin-bottom: 8px;
@@ -110,13 +110,14 @@ power_menu:
110110
.power-menu-popup .button.cancel .icon {
111111
padding: 0;
112112
margin: 0;
113+
max-height: 0;
113114
}
114115
.power-menu-popup .button.cancel .label {
115116
color: #f38ba8;
116117
margin: 0;
117118
}
118119
.power-menu-popup .button.cancel {
119-
height: 32px;
120+
height: 40px;
120121
border-radius: 4px;
121122
}
122123
.power-menu-popup .button.cancel.hover .label {

pyproject.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ authors = [
1111
]
1212
license = { file = "LICENSE" }
1313
readme = "README.md"
14-
requires-python = "==3.12.*"
14+
requires-python = ">= 3.13"
1515
dependencies = [
16-
"PyQt6==6.9.1",
17-
"PyQt6-Qt6==6.9.1",
18-
"PyQt6_sip",
16+
"pyqt6>=6.9.1",
1917
"cerberus",
2018
"humanize",
2119
"psutil",
@@ -26,7 +24,7 @@ dependencies = [
2624
"holidays",
2725
"watchdog",
2826
"pycaw",
29-
"pillow==11.1",
27+
"pillow",
3028
"icoextract",
3129
"qasync",
3230
"obs-websocket-py",
@@ -56,7 +54,9 @@ dependencies = [
5654
dev = [
5755
"pre-commit",
5856
"ruff",
59-
"cx_Freeze==7.2.10"
57+
"cx_freeze",
58+
"pyqt6-stubs",
59+
"types-pillow",
6060
]
6161

6262
[tool.hatch.metadata]

src/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
"silent_level": 1,
5959
"silent": True,
6060
"excludes": ["PySide6", "pydoc_data", "email", "tkinter", "PyQt5", "PySide2", "unittest"],
61-
"bin_excludes": ["Qt6Pdf.dll", "Qt6PdfWidgets.dll"],
61+
"bin_excludes": ["Qt6Pdf.dll", "_avif.cp314-win_amd64.pyd"],
6262
"zip_exclude_packages": ["*"],
6363
"build_exe": "dist",
6464
"include_msvcr": True,

src/core/bar.py

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22

33
import win32con
4-
from PyQt6.QtCore import QEasingCurve, QEvent, QPropertyAnimation, QRect, Qt, pyqtSignal
4+
from PyQt6.QtCore import QEasingCurve, QEvent, QPropertyAnimation, QRect, Qt, QTimer, pyqtSignal
55
from PyQt6.QtGui import QScreen
66
from PyQt6.QtWidgets import QFrame, QGridLayout, QHBoxLayout, QWidget
77

@@ -108,6 +108,11 @@ def __init__(
108108

109109
self.position_bar(init)
110110
self.monitor_hwnd = get_monitor_hwnd(int(self.winId()))
111+
112+
if self._is_auto_width:
113+
self._sync_auto_width()
114+
self._bar_frame.installEventFilter(self)
115+
111116
self._add_widgets(widgets)
112117

113118
if not self._window_flags["windows_app_bar"]:
@@ -138,7 +143,7 @@ def __init__(
138143
self._autohide_manager = AutoHideManager(self, self)
139144
self._autohide_manager.setup_autohide()
140145

141-
self.show()
146+
QTimer.singleShot(0, self.show)
142147

143148
@property
144149
def bar_id(self) -> str:
@@ -151,6 +156,9 @@ def on_geometry_changed(self, geo: QRect) -> None:
151156
if self._autohide_manager and self._autohide_manager.is_enabled():
152157
self._autohide_manager.setup_detection_zone()
153158

159+
if self._is_auto_width:
160+
QTimer.singleShot(0, self._sync_auto_width)
161+
154162
def try_add_app_bar(self, scale_screen_height=False) -> None:
155163
if self.app_bar_manager:
156164
self.app_bar_manager.create_appbar(
@@ -203,8 +211,8 @@ def position_bar(self, init=False) -> None:
203211

204212
scale_state = self.screen().devicePixelRatio() > 1.0
205213

206-
if str(self._dimensions["width"]).lower() == "auto":
207-
bar_width = max(self._bar_frame.sizeHint().width(), 0)
214+
if self._is_auto_width:
215+
bar_width = self._update_auto_width()
208216

209217
elif is_valid_percentage_str(str(self._dimensions["width"])):
210218
percent = percent_to_float(self._dimensions["width"])
@@ -221,6 +229,46 @@ def position_bar(self, init=False) -> None:
221229
self._bar_frame.setGeometry(0, 0, bar_width, bar_height)
222230
self.try_add_app_bar(scale_screen_height=scale_state)
223231

232+
def _update_auto_width(self) -> int:
233+
"""Calculate the current auto width based on the layout's size hint."""
234+
layout = self._bar_frame.layout()
235+
if layout:
236+
layout.activate()
237+
238+
requested = max(self._bar_frame.sizeHint().width(), 0)
239+
available = self.screen().geometry().width() - self._padding["left"] - self._padding["right"]
240+
new_width = min(requested, available)
241+
self._current_auto_width = new_width
242+
return new_width
243+
244+
def _apply_auto_width(self, new_width: int) -> None:
245+
"""Resize and reposition the bar using the supplied auto width."""
246+
if new_width < 0:
247+
return
248+
249+
bar_height = self._dimensions["height"]
250+
screen_geometry = self.screen().geometry()
251+
bar_x, bar_y = self.bar_pos(
252+
new_width,
253+
bar_height,
254+
screen_geometry.width(),
255+
screen_geometry.height(),
256+
)
257+
258+
self.setGeometry(bar_x, bar_y, new_width, bar_height)
259+
self._bar_frame.setGeometry(0, 0, new_width, bar_height)
260+
261+
def _sync_auto_width(self) -> None:
262+
"""Ensure auto width matches the layout after a DPI/geometry."""
263+
if not self._is_auto_width:
264+
return
265+
266+
previous_width = self._current_auto_width
267+
new_width = self._update_auto_width()
268+
269+
if new_width != previous_width or self.width() != new_width:
270+
self._apply_auto_width(new_width)
271+
224272
def _add_widgets(self, widgets: dict[str, list] = None):
225273
bar_layout = QGridLayout()
226274
bar_layout.setContentsMargins(0, 0, 0, 0)
@@ -257,9 +305,6 @@ def _add_widgets(self, widgets: dict[str, list] = None):
257305

258306
self._bar_frame.setLayout(bar_layout)
259307

260-
if self._is_auto_width:
261-
self._bar_frame.installEventFilter(self)
262-
263308
def show_bar(self):
264309
self.setWindowOpacity(0.0)
265310
self.opacity_animation = QPropertyAnimation(self, b"windowOpacity")
@@ -310,24 +355,10 @@ def changeEvent(self, event: QEvent) -> None:
310355

311356
def eventFilter(self, obj, event):
312357
if self._is_auto_width and obj == self._bar_frame and event.type() == QEvent.Type.LayoutRequest:
313-
requested = max(self._bar_frame.sizeHint().width(), 0)
314-
available = self.screen().geometry().width() - self._padding["left"] - self._padding["right"]
315-
new_width = min(requested, available)
316-
if new_width != self._current_auto_width:
317-
self._current_auto_width = new_width
318-
bar_height = self.height()
319-
self.resize(new_width, bar_height)
320-
self._bar_frame.resize(new_width, bar_height)
321-
322-
screen_geometry = self.screen().geometry()
323-
bar_x, bar_y = self.bar_pos(
324-
new_width,
325-
bar_height,
326-
screen_geometry.width(),
327-
screen_geometry.height(),
328-
)
329-
if self.x() != bar_x or self.y() != bar_y:
330-
self.move(bar_x, bar_y)
358+
previous_width = self._current_auto_width
359+
new_width = self._update_auto_width()
360+
if new_width != previous_width:
361+
self._apply_auto_width(new_width)
331362

332363
return super().eventFilter(obj, event)
333364

src/core/bar_helper.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323

2424
from core.utils.controller import exit_application, reload_application
25+
from core.utils.utilities import refresh_widget_style
2526
from core.utils.win32.bindings import DwmGetWindowAttribute
2627
from core.utils.win32.constants import DWMWA_CLOAKED, S_OK
2728
from core.utils.win32.utilities import get_monitor_hwnd, get_window_rect, qmenu_rounded_corners
@@ -437,11 +438,9 @@ def update_theme_class(self):
437438

438439
def _update_styles(self, widget):
439440
"""Update styles for widget and its children by unpolishing and re-polishing"""
440-
widget.style().unpolish(widget)
441-
widget.style().polish(widget)
441+
refresh_widget_style(widget)
442442
for child in widget.findChildren(QWidget):
443-
child.style().unpolish(child)
444-
child.style().polish(child)
443+
refresh_widget_style(child)
445444

446445

447446
class BarContextMenu:

src/core/tray.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ def _load_context_menu(self):
8686
QMenu {
8787
background-color: #202020;
8888
color: #ffffff;
89-
border:1px solid #373b3e;
89+
border:1px solid #303030;
9090
padding:5px 0;
9191
margin:0;
92-
border-radius:4px
92+
border-radius:8px
9393
}
9494
QMenu::item {
9595
margin:0 4px;

src/core/ui/windows/about.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from core.ui.style import apply_button_style, apply_link_button_style
2121
from core.ui.windows.update_dialog import ReleaseFetcher, ReleaseInfo, UpdateDialog
2222
from core.utils.tooltip import set_tooltip
23-
from core.utils.utilities import get_app_identifier, is_valid_qobject
23+
from core.utils.utilities import get_app_identifier, is_valid_qobject, refresh_widget_style
2424
from settings import (
2525
APP_ID,
2626
APP_NAME,
@@ -209,8 +209,7 @@ def _refresh_update_button_style(self) -> None:
209209
state = self._update_button.property("updateState") or "idle"
210210
variant = "primary" if state == "available" else "secondary"
211211
apply_button_style(self._update_button, variant)
212-
self._update_button.style().unpolish(self._update_button)
213-
self._update_button.style().polish(self._update_button)
212+
refresh_widget_style(self._update_button)
214213

215214
def _apply_state(
216215
self,
@@ -332,8 +331,7 @@ def _disable_update_capability(self) -> None:
332331
set_tooltip(self._update_button, config["tooltip"], 0, position="top")
333332

334333
apply_button_style(self._update_button, "secondary")
335-
self._update_button.style().unpolish(self._update_button)
336-
self._update_button.style().polish(self._update_button)
334+
refresh_widget_style(self._update_button)
337335

338336
def showEvent(self, event) -> None:
339337
self._apply_palette()

src/core/ui/windows/update_dialog.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
from core.ui.style import apply_button_style
3232
from core.utils.controller import exit_application
33-
from core.utils.utilities import is_process_running, is_valid_qobject
33+
from core.utils.utilities import is_process_running, is_valid_qobject, refresh_widget_style
3434
from settings import APP_NAME, SCRIPT_PATH
3535

3636
GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/amnweb/yasb/releases/latest"
@@ -487,8 +487,7 @@ def showEvent(self, event) -> None:
487487
def event(self, event) -> bool:
488488
if event.type() == QEvent.Type.PaletteChange:
489489
self._apply_button_styles()
490-
self.changelog_view.style().unpolish(self.changelog_view)
491-
self.changelog_view.style().polish(self.changelog_view)
490+
refresh_widget_style(self.changelog_view)
492491
return super().event(event)
493492

494493
def present(self) -> None:

0 commit comments

Comments
 (0)