日历悬浮窗口

pull/109/head
Maziang 3 months ago
parent 07ac34ee46
commit 2415e09748

@ -0,0 +1,482 @@
# -*- coding: utf-8 -*-
"""
日历悬浮窗口模块
提供一个可拖拽的日历悬浮窗口用于在应用程序中显示和选择日期
"""
import sys
from PyQt5.QtWidgets import (
QWidget, QCalendarWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QFrame
)
from PyQt5.QtCore import QDate, Qt, pyqtSignal, QPoint
from PyQt5.QtGui import QFont
# 导入主题管理器
from .theme_manager import theme_manager
class CalendarFloatingWidget(QWidget):
"""日历悬浮窗口类"""
# 自定义信号
closed = pyqtSignal() # 窗口关闭信号
date_selected = pyqtSignal(str) # 日期字符串信号,用于插入功能
def __init__(self, parent=None):
super().__init__(parent)
self.drag_position = None
self.is_dragging = False
self.setup_ui()
self.setup_connections()
self.setup_theme()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
self.setAttribute(Qt.WA_TranslucentBackground)
def setup_ui(self):
"""设置UI界面"""
# 设置窗口属性
self.setWindowTitle("日历")
self.setFixedSize(360, 320) # 设置窗口大小
# 创建主框架,用于实现圆角和阴影效果
self.main_frame = QFrame()
self.main_frame.setObjectName("mainFrame")
# 主布局
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.addWidget(self.main_frame)
# 内容布局
content_layout = QVBoxLayout(self.main_frame)
content_layout.setContentsMargins(10, 10, 10, 10)
content_layout.setSpacing(8)
# 标题栏
title_layout = QHBoxLayout()
title_layout.setContentsMargins(0, 0, 0, 0)
title_layout.setSpacing(0)
self.title_label = QLabel("日历")
self.title_label.setFont(QFont("Arial", 12, QFont.Bold))
title_layout.addWidget(self.title_label)
title_layout.addStretch()
# 添加一个小的固定空间,使关闭按钮向左移动
title_layout.addSpacing(25) # 向左移动25个单位
# 关闭按钮
self.close_btn = QPushButton("×")
self.close_btn.setFixedSize(20, 20)
self.close_btn.setObjectName("closeButton")
title_layout.addWidget(self.close_btn)
content_layout.addLayout(title_layout)
# 分隔线
separator = QFrame()
separator.setObjectName("separator")
separator.setFixedHeight(1)
content_layout.addWidget(separator)
# 日历控件
self.calendar = QCalendarWidget()
self.calendar.setGridVisible(True)
self.calendar.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader)
self.calendar.setNavigationBarVisible(True)
content_layout.addWidget(self.calendar)
# 当前日期显示
self.date_label = QLabel()
self.date_label.setAlignment(Qt.AlignCenter)
self.date_label.setFont(QFont("Arial", 10))
self.date_label.setObjectName("dateLabel")
self.update_date_label()
content_layout.addWidget(self.date_label)
# 操作按钮
button_layout = QHBoxLayout()
button_layout.setContentsMargins(0, 0, 0, 0)
button_layout.setSpacing(6)
self.today_btn = QPushButton("今天")
self.today_btn.setObjectName("todayButton")
button_layout.addWidget(self.today_btn)
self.insert_btn = QPushButton("插入")
self.insert_btn.setObjectName("insertButton")
button_layout.addWidget(self.insert_btn)
button_layout.addStretch()
self.clear_btn = QPushButton("清除")
self.clear_btn.setObjectName("clearButton")
button_layout.addWidget(self.clear_btn)
content_layout.addLayout(button_layout)
def setup_connections(self):
"""设置信号连接"""
self.calendar.clicked.connect(self.on_date_selected)
self.today_btn.clicked.connect(self.on_today_clicked)
self.clear_btn.clicked.connect(self.on_clear_clicked)
self.close_btn.clicked.connect(self.close_window)
self.insert_btn.clicked.connect(self.on_insert_clicked)
def setup_theme(self):
"""设置主题"""
# 连接主题切换信号
theme_manager.theme_changed.connect(self.on_theme_changed)
# 应用当前主题
self.apply_theme()
def apply_theme(self):
"""应用主题样式"""
is_dark = theme_manager.is_dark_theme()
colors = theme_manager.get_current_theme_colors()
if is_dark:
# 深色主题样式
self.main_frame.setStyleSheet(f"""
QFrame#mainFrame {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 10px;
}}
QLabel {{
color: {colors['text']};
background-color: transparent;
padding: 4px 6px;
margin: 2px;
}}
QLabel#dateLabel {{
color: {colors['text_secondary']};
font-size: 11px;
padding: 4px 6px;
margin: 2px;
}}
QFrame#separator {{
background-color: {colors['border']};
}}
QPushButton#closeButton {{
background-color: rgba(255, 255, 255, 0.1);
border: none;
color: {colors['text']};
font-size: 18px;
font-weight: bold;
border-radius: 6px;
padding: 2px 4px;
}}
QPushButton#closeButton:hover {{
background-color: #e81123;
color: white;
border-radius: 6px;
}}
QPushButton#todayButton, QPushButton#clearButton, QPushButton#insertButton {{
background-color: {colors['accent']};
color: white;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 11px;
font-weight: 500;
}}
QPushButton#todayButton:hover, QPushButton#clearButton:hover, QPushButton#insertButton:hover {{
background-color: {colors['accent_hover']};
}}
""")
# 更新日历控件样式
self.calendar.setStyleSheet(f"""
QCalendarWidget {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 4px;
}}
QCalendarWidget QToolButton {{
height: 30px;
width: 80px;
color: {colors['text']};
font-size: 12px;
font-weight: bold;
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 4px;
}}
QCalendarWidget QToolButton:hover {{
background-color: {colors['surface_hover']};
}}
QCalendarWidget QMenu {{
width: 150px;
left: 20px;
color: {colors['text']};
font-size: 12px;
background-color: {colors['surface']};
border: 1px solid {colors['border']};
}}
QCalendarWidget QSpinBox {{
width: 80px;
font-size: 12px;
background-color: {colors['surface']};
selection-background-color: {colors['accent']};
selection-color: white;
border: 1px solid {colors['border']};
border-radius: 4px;
color: {colors['text']};
}}
QCalendarWidget QSpinBox::up-button {{
subcontrol-origin: border;
subcontrol-position: top right;
width: 20px;
}}
QCalendarWidget QSpinBox::down-button {{
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 20px;
}}
QCalendarWidget QSpinBox::up-arrow {{
width: 10px;
height: 10px;
}}
QCalendarWidget QSpinBox::down-arrow {{
width: 10px;
height: 10px;
}}
QCalendarWidget QWidget {{
alternate-background-color: {colors['surface']};
}}
QCalendarWidget QAbstractItemView:enabled {{
font-size: 12px;
selection-background-color: {colors['accent']};
selection-color: white;
background-color: {colors['surface']};
color: {colors['text']};
}}
QCalendarWidget QWidget#qt_calendar_navigationbar {{
background-color: {colors['surface']};
}}
""")
else:
# 浅色主题样式
self.main_frame.setStyleSheet(f"""
QFrame#mainFrame {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}}
QLabel {{
color: {colors['text']};
background-color: transparent;
padding: 4px 6px;
margin: 2px;
}}
QLabel#dateLabel {{
color: {colors['text_secondary']};
font-size: 11px;
padding: 4px 6px;
margin: 2px;
}}
QFrame#separator {{
background-color: {colors['border']};
}}
QPushButton#closeButton {{
background-color: rgba(0, 0, 0, 0.05);
border: none;
color: {colors['text']};
font-size: 18px;
font-weight: bold;
border-radius: 6px;
padding: 2px 4px;
}}
QPushButton#closeButton:hover {{
background-color: #e81123;
color: white;
border-radius: 6px;
}}
QPushButton#todayButton, QPushButton#clearButton, QPushButton#insertButton {{
background-color: {colors['accent']};
color: white;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 11px;
font-weight: 500;
}}
QPushButton#todayButton:hover, QPushButton#clearButton:hover, QPushButton#insertButton:hover {{
background-color: {colors['accent_hover']};
}}
""")
# 更新日历控件样式
self.calendar.setStyleSheet(f"""
QCalendarWidget {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 4px;
}}
QCalendarWidget QToolButton {{
height: 30px;
width: 80px;
color: {colors['text']};
font-size: 12px;
font-weight: bold;
background-color: {colors['surface']};
border: 1px solid {colors['border']};
border-radius: 4px;
}}
QCalendarWidget QToolButton:hover {{
background-color: {colors['surface_hover']};
}}
QCalendarWidget QMenu {{
width: 150px;
left: 20px;
color: {colors['text']};
font-size: 12px;
background-color: {colors['surface']};
border: 1px solid {colors['border']};
}}
QCalendarWidget QSpinBox {{
width: 80px;
font-size: 12px;
background-color: {colors['surface']};
selection-background-color: {colors['accent']};
selection-color: white;
border: 1px solid {colors['border']};
border-radius: 4px;
color: {colors['text']};
}}
QCalendarWidget QSpinBox::up-button {{
subcontrol-origin: border;
subcontrol-position: top right;
width: 20px;
}}
QCalendarWidget QSpinBox::down-button {{
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 20px;
}}
QCalendarWidget QSpinBox::up-arrow {{
width: 10px;
height: 10px;
}}
QCalendarWidget QSpinBox::down-arrow {{
width: 10px;
height: 10px;
}}
QCalendarWidget QWidget {{
alternate-background-color: {colors['surface']};
}}
QCalendarWidget QAbstractItemView:enabled {{
font-size: 12px;
selection-background-color: {colors['accent']};
selection-color: white;
background-color: {colors['surface']};
color: {colors['text']};
}}
QCalendarWidget QWidget#qt_calendar_navigationbar {{
background-color: {colors['surface']};
}}
""")
def on_theme_changed(self, is_dark):
"""主题切换槽函数"""
self.apply_theme()
def mousePressEvent(self, event):
"""鼠标按下事件,用于拖拽"""
if event.button() == Qt.LeftButton:
# 检查是否点击在标题栏区域
if event.pos().y() <= 40: # 假设标题栏高度为40像素
self.is_dragging = True
self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
event.accept()
def mouseMoveEvent(self, event):
"""鼠标移动事件,用于拖拽"""
if self.is_dragging and event.buttons() == Qt.LeftButton:
self.move(event.globalPos() - self.drag_position)
event.accept()
def mouseReleaseEvent(self, event):
"""鼠标释放事件"""
self.is_dragging = False
def on_date_selected(self, date):
"""日期选择事件"""
self.update_date_label(date)
def on_today_clicked(self):
"""今天按钮点击事件"""
today = QDate.currentDate()
self.calendar.setSelectedDate(today)
self.update_date_label(today)
def on_clear_clicked(self):
"""清除按钮点击事件"""
self.calendar.setSelectedDate(QDate())
self.date_label.setText("未选择日期")
def on_insert_clicked(self):
"""插入按钮点击事件"""
selected_date = self.calendar.selectedDate()
if selected_date.isValid():
# 发送信号,将选中的日期传递给主窗口
date_str = selected_date.toString("yyyy年MM月dd日 dddd")
self.date_selected.emit(date_str)
def update_date_label(self, date=None):
"""更新日期显示标签"""
if date is None:
date = self.calendar.selectedDate()
if date.isValid():
date_str = date.toString("yyyy年MM月dd日 (ddd)")
self.date_label.setText(f"选中日期: {date_str}")
else:
self.date_label.setText("未选择日期")
def get_selected_date(self):
"""获取选中的日期"""
return self.calendar.selectedDate()
def set_selected_date(self, date):
"""设置选中的日期"""
if isinstance(date, str):
date = QDate.fromString(date, "yyyy-MM-dd")
self.calendar.setSelectedDate(date)
self.update_date_label(date)
def close_window(self):
"""关闭窗口 - 只是隐藏而不是销毁"""
try:
self.closed.emit()
self.hide() # 隐藏窗口而不是销毁
except Exception as e:
print(f"Error in close_window: {e}")
def main():
"""测试函数"""
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
# 创建并显示窗口
widget = CalendarFloatingWidget()
widget.show()
# 移动到屏幕中心
screen_geometry = app.desktop().screenGeometry()
widget.move(
(screen_geometry.width() - widget.width()) // 2,
(screen_geometry.height() - widget.height()) // 2
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()

@ -711,10 +711,17 @@ class WordRibbon(QFrame):
self.floating_quote_btn.setStyleSheet("QPushButton { font-size: 11px; padding: 5px; }")
self.floating_quote_btn.setToolTip("切换每日谏言悬浮窗口")
# 日历悬浮窗口按钮
self.floating_calendar_btn = QPushButton("📅 悬浮")
self.floating_calendar_btn.setFixedSize(60, 30)
self.floating_calendar_btn.setStyleSheet("QPushButton { font-size: 11px; padding: 5px; }")
self.floating_calendar_btn.setToolTip("切换日历悬浮窗口")
control_layout.addWidget(self.city_combo)
control_layout.addWidget(self.refresh_weather_btn)
control_layout.addWidget(self.floating_weather_btn)
control_layout.addWidget(self.floating_quote_btn)
control_layout.addWidget(self.floating_calendar_btn)
# 添加右侧弹性空间,确保内容居中
control_layout.addStretch()

@ -21,6 +21,7 @@ from input_handler.input_processor import InputProcessor
from ui.calendar_widget import CalendarWidget
from ui.weather_floating_widget import WeatherFloatingWidget
from ui.quote_floating_widget import QuoteFloatingWidget
from ui.calendar_floating_widget import CalendarFloatingWidget
# 导入主题管理器
from ui.theme_manager import theme_manager
@ -116,13 +117,17 @@ class WordStyleMainWindow(QMainWindow):
self.weather_floating_widget.closed.connect(self.on_weather_floating_closed)
self.weather_floating_widget.refresh_requested.connect(self.refresh_weather)
# 初始化每日谏言悬浮窗口
self.quote_floating_widget = QuoteFloatingWidget(self)
self.quote_floating_widget.hide() # 默认隐藏
self.quote_floating_widget.closed.connect(self.on_quote_floating_closed)
self.quote_floating_widget.refresh_requested.connect(self.refresh_daily_quote)
self.quote_floating_widget.insert_requested.connect(self.insert_quote_to_cursor)
self.calendar_floating_widget = CalendarFloatingWidget(self)
self.calendar_floating_widget.hide() # 默认隐藏
self.calendar_floating_widget.closed.connect(self.on_calendar_floating_closed)
self.calendar_floating_widget.date_selected.connect(self.insert_date_to_cursor)
# 设置窗口属性
self.setWindowTitle("文档1 - MagicWord")
self.setGeometry(100, 100, 1200, 800)
@ -149,6 +154,14 @@ class WordStyleMainWindow(QMainWindow):
self.ribbon.on_refresh_weather = self.refresh_weather
self.ribbon.on_city_changed = self.on_city_changed
# 连接Ribbon的悬浮窗口按钮信号
if hasattr(self.ribbon, 'floating_weather_btn'):
self.ribbon.floating_weather_btn.clicked.connect(self.toggle_floating_weather)
if hasattr(self.ribbon, 'floating_quote_btn'):
self.ribbon.floating_quote_btn.clicked.connect(self.toggle_floating_quote)
if hasattr(self.ribbon, 'floating_calendar_btn'):
self.ribbon.floating_calendar_btn.clicked.connect(self.toggle_floating_calendar)
# 初始化时刷新天气
self.refresh_weather()
@ -723,6 +736,11 @@ class WordStyleMainWindow(QMainWindow):
toggle_floating_quote_action = QAction('每日谏言悬浮窗口', self)
toggle_floating_quote_action.triggered.connect(self.toggle_floating_quote)
weather_menu.addAction(toggle_floating_quote_action)
# 日历悬浮窗口切换动作
toggle_floating_calendar_action = QAction('日历悬浮窗口', self)
toggle_floating_calendar_action.triggered.connect(self.toggle_floating_calendar)
weather_menu.addAction(toggle_floating_calendar_action)
# 插入菜单
insert_menu = menubar.addMenu('插入(I)')
@ -1800,6 +1818,22 @@ class WordStyleMainWindow(QMainWindow):
def on_quote_floating_closed(self):
"""每日谏言悬浮窗口关闭时的处理"""
self.status_bar.showMessage("每日谏言悬浮窗口已关闭", 2000)
def toggle_floating_calendar(self):
"""切换日历悬浮窗口的显示/隐藏状态"""
if hasattr(self, 'calendar_floating_widget'):
if self.calendar_floating_widget.isVisible():
self.calendar_floating_widget.hide()
self.status_bar.showMessage("日历悬浮窗口已隐藏", 2000)
else:
self.calendar_floating_widget.show()
# 确保窗口在屏幕内
self.calendar_floating_widget.move(100, 100)
self.status_bar.showMessage("日历悬浮窗口已显示", 2000)
def on_calendar_floating_closed(self):
"""日历悬浮窗口关闭事件"""
self.status_bar.showMessage("日历悬浮窗口已关闭", 2000)
def insert_quote_to_cursor(self, quote_text):
"""将古诗句插入到光标位置"""
@ -1814,6 +1848,18 @@ class WordStyleMainWindow(QMainWindow):
# 从文本中提取诗句部分用于显示
quote_only = quote_text.split(" —— ")[0] if " —— " in quote_text else quote_text
self.status_bar.showMessage(f"已插入古诗句: {quote_only}", 3000)
def insert_date_to_cursor(self, date_str):
"""在光标位置插入日期"""
try:
# 在光标位置插入日期
cursor = self.text_edit.textCursor()
cursor.insertText(date_str)
# 更新状态栏
self.status_bar.showMessage(f"已插入日期: {date_str}", 2000)
except Exception as e:
print(f"插入日期时出错: {e}")
def toggle_weather_tools(self, checked):
"""切换天气工具组显示"""

Loading…
Cancel
Save