古诗句悬浮窗口

Maziang 3 months ago
parent efaf1ae33c
commit bfdb9d124c

@ -0,0 +1,415 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
每日谏言悬浮窗口
"""
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QFrame, QGraphicsDropShadowEffect)
from PyQt5.QtCore import Qt, QPoint, pyqtSignal
from PyQt5.QtGui import QFont, QColor
class QuoteFloatingWidget(QWidget):
"""每日谏言悬浮窗口"""
# 定义信号
closed = pyqtSignal() # 窗口关闭信号
refresh_requested = pyqtSignal() # 刷新请求信号
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
self.setAttribute(Qt.WA_TranslucentBackground)
# 初始化变量
self.is_dragging = False
self.drag_position = QPoint()
# 设置默认谏言数据
self.quote_data = {
"quote": "书山有路勤为径,学海无涯苦作舟。",
"author": "韩愈",
"source": "《古今贤文·劝学篇》"
}
# 初始化UI
self.init_ui()
self.setup_styles()
self.apply_theme(is_dark=True) # 默认使用深色主题
def init_ui(self):
"""初始化UI"""
# 主框架
self.main_frame = QFrame(self)
self.main_frame.setObjectName("mainFrame")
self.main_frame.setFixedSize(360, 200) # 设置窗口大小
main_layout = QVBoxLayout(self.main_frame)
main_layout.setContentsMargins(12, 12, 12, 12)
main_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")
self.close_btn.clicked.connect(self.close_window)
title_layout.addWidget(self.close_btn)
main_layout.addLayout(title_layout)
# 分隔线
separator = QFrame()
separator.setObjectName("separator")
separator.setFixedHeight(1)
main_layout.addWidget(separator)
# 谏言内容区域
content_layout = QVBoxLayout()
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(6)
# 谏言文本
self.quote_label = QLabel()
self.quote_label.setObjectName("quoteLabel")
self.quote_label.setWordWrap(True)
self.quote_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
content_layout.addWidget(self.quote_label)
# 作者信息
self.author_label = QLabel()
self.author_label.setObjectName("authorLabel")
self.author_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
content_layout.addWidget(self.author_label)
main_layout.addLayout(content_layout)
main_layout.addStretch()
# 底部按钮区域
bottom_layout = QHBoxLayout()
bottom_layout.setContentsMargins(0, 0, 0, 0)
bottom_layout.setSpacing(8)
# 刷新按钮
self.refresh_btn = QPushButton("换一句")
self.refresh_btn.setObjectName("refreshButton")
self.refresh_btn.clicked.connect(self.on_refresh_clicked)
bottom_layout.addWidget(self.refresh_btn)
bottom_layout.addStretch()
main_layout.addLayout(bottom_layout)
# 设置主布局
outer_layout = QVBoxLayout(self)
outer_layout.setContentsMargins(0, 0, 0, 0)
outer_layout.addWidget(self.main_frame)
# 更新显示
self.update_quote()
def setup_styles(self):
"""设置样式"""
pass # 样式将在apply_theme中设置
def apply_theme(self, is_dark=True):
"""应用主题"""
if is_dark:
# 深色主题配色
colors = {
'surface': '#2d2d2d',
'border': '#444444',
'text': '#ffffff',
'text_secondary': '#cccccc',
'accent': '#4CAF50',
'accent_hover': '#45a049',
'button_hover': '#555555',
'error': '#f44336'
}
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.3);
}}
QLabel {{
color: {colors['text']};
background-color: transparent;
padding: 4px 6px;
margin: 2px;
}}
QLabel#quoteLabel {{
color: {colors['text']};
font-size: 14px;
font-weight: 500;
padding: 6px 8px;
margin: 3px;
}}
QLabel#authorLabel {{
color: {colors['text_secondary']};
font-size: 12px;
font-style: italic;
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#refreshButton {{
background-color: {colors['accent']};
color: white;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 11px;
font-weight: 500;
}}
QPushButton#refreshButton:hover {{
background-color: {colors['accent_hover']};
}}
""")
else:
# 浅色主题配色
colors = {
'surface': '#ffffff',
'border': '#dddddd',
'text': '#333333',
'text_secondary': '#666666',
'accent': '#4CAF50',
'accent_hover': '#45a049',
'button_hover': '#f0f0f0',
'error': '#f44336'
}
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#quoteLabel {{
color: {colors['text']};
font-size: 14px;
font-weight: 500;
padding: 6px 8px;
margin: 3px;
}}
QLabel#authorLabel {{
color: {colors['text_secondary']};
font-size: 12px;
font-style: italic;
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#refreshButton {{
background-color: {colors['accent']};
color: white;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 11px;
font-weight: 500;
}}
QPushButton#refreshButton:hover {{
background-color: {colors['accent_hover']};
}}
""")
def update_quote(self, quote_data=None):
"""更新谏言显示"""
if quote_data:
self.quote_data = quote_data
else:
# 如果没有提供数据,使用默认数据
if not hasattr(self, 'quote_data'):
self.quote_data = {
"quote": "书山有路勤为径,学海无涯苦作舟。",
"author": "韩愈",
"source": "《古今贤文·劝学篇》"
}
# 更新显示
self.quote_label.setText(self.quote_data["quote"])
author_info = f"{self.quote_data['author']}"
if self.quote_data.get("source"):
author_info += f"{self.quote_data['source']}"
self.author_label.setText(author_info)
def on_refresh_clicked(self):
"""刷新按钮点击事件"""
# 发送刷新请求信号
self.refresh_requested.emit()
# 同时直接获取新的内容并更新显示
self.fetch_and_update_quote()
def fetch_and_update_quote(self):
"""获取新的谏言内容并更新显示"""
try:
# 尝试获取古诗词
import requests
import random
try:
# 使用古诗词·一言API
response = requests.get("https://v1.jinrishici.com/all.json", timeout=5, verify=False)
if response.status_code == 200:
data = response.json()
content = data.get('content', '')
author = data.get('author', '佚名')
origin = data.get('origin', '')
if content:
quote_data = {
"quote": content,
"author": author,
"source": origin
}
self.update_quote(quote_data)
return
except Exception as e:
print(f"获取古诗词失败: {e}")
# 如果古诗词获取失败使用备用API
try:
# 使用每日一言API
response = requests.get("https://api.nxvav.cn/api/yiyan?json=true", timeout=5, verify=False)
if response.status_code == 200:
data = response.json()
yiyan = data.get('yiyan', '')
nick = data.get('nick', '佚名')
if yiyan:
quote_data = {
"quote": yiyan,
"author": nick,
"source": ""
}
self.update_quote(quote_data)
return
except Exception as e:
print(f"获取每日一言失败: {e}")
# 如果API都失败使用预设内容
quotes = [
{"quote": "学而时习之,不亦说乎?", "author": "孔子", "source": "《论语》"},
{"quote": "千里之行,始于足下。", "author": "老子", "source": "《道德经》"},
{"quote": "天行健,君子以自强不息。", "author": "佚名", "source": "《周易》"},
{"quote": "书山有路勤为径,学海无涯苦作舟。", "author": "韩愈", "source": "《古今贤文·劝学篇》"},
{"quote": "山重水复疑无路,柳暗花明又一村。", "author": "陆游", "source": "《游山西村》"}
]
# 随机选择一个名言
new_quote = random.choice(quotes)
self.update_quote(new_quote)
except Exception as e:
print(f"获取新谏言失败: {e}")
# 出错时显示默认内容
default_quote = {
"quote": "书山有路勤为径,学海无涯苦作舟。",
"author": "韩愈",
"source": "《古今贤文·劝学篇》"
}
self.update_quote(default_quote)
def close_window(self):
"""关闭窗口 - 只是隐藏而不是销毁"""
try:
self.closed.emit()
self.hide() # 隐藏窗口而不是销毁
except Exception as e:
print(f"Error in close_window: {e}")
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 main():
"""测试函数"""
app = QApplication(sys.argv)
# 创建并显示窗口
widget = QuoteFloatingWidget()
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()

@ -34,6 +34,7 @@ class WordRibbon(QFrame):
self.weather_group = None # 天气组件组
self.quote_visible = False # 每日一言组件显示状态
self.quote_group = None # 每日一言组件组
self.current_quote_type = "普通箴言" # 每日一言类型
self.ribbon_layout = None # 功能区布局
self.setup_ui()
@ -704,9 +705,16 @@ class WordRibbon(QFrame):
self.floating_weather_btn.setStyleSheet("QPushButton { font-size: 11px; padding: 5px; }")
self.floating_weather_btn.setToolTip("切换天气悬浮窗口")
# 每日谏言悬浮窗口按钮
self.floating_quote_btn = QPushButton("📜 悬浮")
self.floating_quote_btn.setFixedSize(60, 30)
self.floating_quote_btn.setStyleSheet("QPushButton { font-size: 11px; padding: 5px; }")
self.floating_quote_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.addStretch()

@ -20,6 +20,7 @@ from file_parser import FileParser
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.theme_manager import theme_manager
@ -115,6 +116,12 @@ 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.setWindowTitle("文档1 - MagicWord")
self.setGeometry(100, 100, 1200, 800)
@ -710,6 +717,11 @@ class WordStyleMainWindow(QMainWindow):
toggle_floating_weather_action = QAction('天气悬浮窗口', self)
toggle_floating_weather_action.triggered.connect(self.toggle_floating_weather)
weather_menu.addAction(toggle_floating_weather_action)
# 每日谏言悬浮窗口切换动作
toggle_floating_quote_action = QAction('每日谏言悬浮窗口', self)
toggle_floating_quote_action.triggered.connect(self.toggle_floating_quote)
weather_menu.addAction(toggle_floating_quote_action)
# 插入菜单
insert_menu = menubar.addMenu('插入(I)')
@ -946,9 +958,10 @@ class WordStyleMainWindow(QMainWindow):
self.ribbon.city_combo.currentTextChanged.connect(self.on_city_changed)
if hasattr(self.ribbon, 'refresh_weather_btn'):
self.ribbon.refresh_weather_btn.clicked.connect(self.refresh_weather)
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, 'calendar_widget'):
@ -1771,6 +1784,22 @@ class WordStyleMainWindow(QMainWindow):
"""天气悬浮窗口关闭时的处理"""
self.status_bar.showMessage("天气悬浮窗口已关闭", 2000)
def toggle_floating_quote(self):
"""切换每日谏言悬浮窗口显示/隐藏"""
if hasattr(self, 'quote_floating_widget'):
if self.quote_floating_widget.isVisible():
self.quote_floating_widget.hide()
self.status_bar.showMessage("每日谏言悬浮窗口已隐藏", 2000)
else:
self.quote_floating_widget.show()
# 确保窗口在屏幕内
self.quote_floating_widget.move(100, 100)
self.status_bar.showMessage("每日谏言悬浮窗口已显示", 2000)
def on_quote_floating_closed(self):
"""每日谏言悬浮窗口关闭时的处理"""
self.status_bar.showMessage("每日谏言悬浮窗口已关闭", 2000)
def toggle_weather_tools(self, checked):
"""切换天气工具组显示"""
if checked:
@ -1805,6 +1834,12 @@ class WordStyleMainWindow(QMainWindow):
if hasattr(self, 'ribbon'):
# 直接调用WordRibbon中的刷新方法
self.ribbon.on_refresh_quote()
# 同时更新浮动窗口中的内容(如果浮动窗口存在且可见)
if hasattr(self, 'quote_floating_widget') and self.quote_floating_widget.isVisible():
# 调用浮动窗口的获取新内容方法
if hasattr(self.quote_floating_widget, 'fetch_and_update_quote'):
self.quote_floating_widget.fetch_and_update_quote()
def on_quote_fetched(self, quote_data):
"""处理名言获取成功"""

Loading…
Cancel
Save