更新0.02版本

This commit is contained in:
2026-04-07 10:48:27 +08:00
parent 77f718cca6
commit 3051f6ca49
19 changed files with 345 additions and 37 deletions
+58 -11
View File
@@ -43,12 +43,25 @@ def _load_logo_pixmap(path):
def _win32_set_ellipse_window_rgn(hwnd, w, h):
"""用系统区域裁切 HWND。"""
"""用系统区域裁切 HWND,w/h 为逻辑像素,内部换算为物理像素。"""
if not hwnd or w < 2 or h < 2:
return
gdi32 = ctypes.windll.gdi32
user32 = ctypes.windll.user32
hrgn = gdi32.CreateEllipticRgn(0, 0, w, h)
# 获取窗口所在显示器的 DPI,换算为物理像素
try:
MONITOR_DEFAULTTONEAREST = 2
hmon = ctypes.windll.user32.MonitorFromWindow(wintypes.HWND(hwnd), MONITOR_DEFAULTTONEAREST)
dpi_x = ctypes.c_uint(0)
dpi_y = ctypes.c_uint(0)
MDT_EFFECTIVE_DPI = 0
ctypes.windll.shcore.GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, ctypes.byref(dpi_x), ctypes.byref(dpi_y))
scale = dpi_x.value / 96.0
except Exception:
scale = 1.0
pw = max(2, int(w * scale))
ph = max(2, int(h * scale))
hrgn = gdi32.CreateEllipticRgn(0, 0, pw, ph)
if not hrgn:
return
user32.SetWindowRgn(hwnd, hrgn, True)
@@ -139,14 +152,21 @@ class FloatBall(QWidget):
_win32_set_ellipse_window_rgn(wid, self.width(), self.height())
_win32_disable_dwm_rounded_shell(wid)
def moveEvent(self, event):
super().moveEvent(event)
# 跨屏移动后 DPI 可能变化,重新设置物理像素 region
if sys.platform == "win32":
wid = int(self.winId())
if wid:
_win32_set_ellipse_window_rgn(wid, self.width(), self.height())
def _apply_window_flags(self):
flags = (
Qt.WindowType.FramelessWindowHint
| Qt.WindowType.Tool
| Qt.WindowType.NoDropShadowWindowHint
| Qt.WindowType.WindowStaysOnTopHint
)
if self._stays_on_top:
flags |= Qt.WindowType.WindowStaysOnTopHint
self.setWindowFlags(flags)
def _get_glow(self): return self._glow
@@ -154,6 +174,14 @@ class FloatBall(QWidget):
from PyQt6.QtCore import pyqtProperty
_glow_prop = pyqtProperty(float, _get_glow, _set_glow)
def _current_screen_geometry(self):
"""返回当前窗口所在屏幕的可用区域,找不到时回退到主屏幕。"""
center = self.geometry().center()
screen = QApplication.screenAt(center)
if screen is None:
screen = QApplication.primaryScreen()
return screen.availableGeometry()
def _place_default(self):
screen = QApplication.primaryScreen().availableGeometry()
self.move(
@@ -161,6 +189,19 @@ class FloatBall(QWidget):
screen.top() + screen.height() // 2 - self.height() // 2,
)
def place_on_screen_of(self, widget):
"""将悬浮球放到指定 widget 所在屏幕的右侧中央。"""
# frameGeometry().center() 直接是全局坐标,对顶层窗口最可靠
global_center = widget.frameGeometry().center()
screen_obj = QApplication.screenAt(global_center)
if screen_obj is None:
screen_obj = QApplication.primaryScreen()
screen = screen_obj.availableGeometry()
self.move(
screen.right() - self.width() - 20,
screen.top() + screen.height() // 2 - self.height() // 2,
)
def _paint_radius_outer(self):
w, h = self.width(), self.height()
return min(w, h) * 0.5 - 0.5
@@ -224,15 +265,17 @@ class FloatBall(QWidget):
# 3. Logo(cover 铺满圆)
if not self._logo.isNull():
p.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True)
cover = max(1, int(2 * r))
side = max(1, int(2 * r))
scaled = self._logo.scaled(
cover, cover,
side, side,
Qt.AspectRatioMode.KeepAspectRatioByExpanding,
Qt.TransformationMode.SmoothTransformation
)
lx = int(cx - scaled.width() / 2)
ly = int(cy - scaled.height() / 2)
p.drawPixmap(lx, ly, scaled)
# 居中裁切到 side×side,避免 Expanding 后图片偏大导致偏移
ox = (scaled.width() - side) // 2
oy = (scaled.height() - side) // 2
cropped = scaled.copy(ox, oy, side, side)
p.drawPixmap(int(cx - side / 2), int(cy - side / 2), cropped)
# 4. 覆在图上的雾面(毛玻璃「蒙在内容上」)
veil = QLinearGradient(cx, cy - r, cx, cy + r)
@@ -280,7 +323,11 @@ class FloatBall(QWidget):
if self._dragging:
new_pos = self.pos() + delta
self._drag_start = event.globalPosition().toPoint()
screen = QApplication.primaryScreen().availableGeometry()
# 用鼠标当前位置所在屏幕来限制边界,支持多显示器拖动
cursor_screen = QApplication.screenAt(event.globalPosition().toPoint())
if cursor_screen is None:
cursor_screen = QApplication.primaryScreen()
screen = cursor_screen.availableGeometry()
new_pos.setX(max(screen.left(), min(new_pos.x(), screen.right() - self.width())))
new_pos.setY(max(screen.top(), min(new_pos.y(), screen.bottom() - self.height())))
self.move(new_pos)
@@ -307,7 +354,7 @@ class FloatBall(QWidget):
self._glow_anim.start()
def _snap_to_edge(self):
screen = QApplication.primaryScreen().availableGeometry()
screen = self._current_screen_geometry()
cx = self.x() + self.width() // 2
cy = self.y() + self.height() // 2
dists = {