self,top,blank,parent区别

本文介绍了在HTML中使用不同目标属性(如_blank、_self、_parent、_top)来控制链接打开方式的方法。这些属性决定了链接是在新窗口、当前窗口还是特定框架中打开。

_blank,在新窗口显示目标网页
_self,在当前窗口显示目标网页

_parent,框架网页中当前整个窗口位置显示目标网页
_top,框架网页中在上部窗口中显示目标网页

修改问题:未解析的引用 'core'未解析的引用 'InputAnalyzer'未解析的引用 'DialogManager'PEP 8: E302 expected 2 blank lines, found 1实例特性 _parent_window 在 __init__ 外部定义PEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E128 continuation line under-indented for visual indentPEP 8: E722 do not use bare 'except'异常子句过于宽泛PEP 8: E305 expected 2 blank lines after class or function definition, found 1PEP 8: W292 no newline at end of file代码#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 对话框管理模块 (dialog_manager.py) 功能: 1. 统一管理系统中的所有对话框 2. 提供标准化的对话框创建接口 3. 处理用户与对话框的交互逻辑 典型对话框类型: - 参数设置对话框 - 分析结果展示对话框 - 错误提示对话框 - 确认操作对话框 """ import tkinter as tk from tkinter import messagebox, simpledialog, filedialog from typing import Optional, Callable, Any, Dict # main.py from core import InputAnalyzer, DialogManager analyzer = InputAnalyzer() dm = DialogManager() print(analyzer.analyze("测试输入")) dm.show_dialog("欢迎使用系统") class DialogManager: """对话框管理器 (单例模式)""" _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._init_dialogs() return cls._instance def _init_dialogs(self): """初始化对话框状态""" self._active_dialogs = {} self._parent_window = None def set_parent_window(self, parent: tk.Toplevel): """设置父窗口""" self._parent_window = parent def show_message(self, title: str, message: str, dialog_type: str = "info"): """ 显示消息对话框 :param title: 对话框标题 :param message: 显示的消息内容 :param dialog_type: 类型 (info/warning/error) """ if dialog_type == "info": messagebox.showinfo(title, message, parent=self._parent_window) elif dialog_type == "warning": messagebox.showwarning(title, message, parent=self._parent_window) else: messagebox.showerror(title, message, parent=self._parent_window) def get_user_input(self, title: str, prompt: str, initial_value: str = "") -> Optional[str]: """ 获取用户输入 :return: 用户输入的字符串或None(取消时) """ return simpledialog.askstring( title, prompt, initialvalue=initial_value, parent=self._parent_window ) def confirm_action(self, title: str, message: str) -> bool: """ 显示确认对话框 :return: True表示确认,False表示取消 """ return messagebox.askyesno( title, message, parent=self._parent_window ) def show_analysis_result(self, result_data: Dict[str, Any], callback: Optional[Callable] = None): """ 显示分析结果对话框 :param result_data: 包含分析结果的数据字典 :param callback: 用户操作后的回调函数 """ dialog = tk.Toplevel(self._parent_window) dialog.title("分析结果") # 添加结果展示组件 text_area = tk.Text(dialog, width=60, height=20) text_area.pack(padx=10, pady=10) # 格式化显示结果 text_area.insert(tk.END, "=== 分析结果 ===\n\n") for key, value in result_data.items(): text_area.insert(tk.END, f"{key}: {value}\n") # 添加操作按钮 button_frame = tk.Frame(dialog) button_frame.pack(pady=5) tk.Button( button_frame, text="保存结果", command=lambda: self._save_result(result_data, callback) ).pack(side=tk.LEFT, padx=5) tk.Button( button_frame, text="关闭", command=dialog.destroy ).pack(side=tk.LEFT, padx=5) self._active_dialogs["result"] = dialog def _save_result(self, result_data: Dict[str, Any], callback: Optional[Callable]): """保存结果到文件""" file_path = filedialog.asksaveasfilename( defaultextension=".txt", filetypes=[("Text files", "*.txt"), ("All files", "*.*")], parent=self._parent_window ) if file_path: try: with open(file_path, 'w') as f: for key, value in result_data.items(): f.write(f"{key}: {value}\n") if callback: callback(True, file_path) self.show_message("保存成功", "结果已保存到文件") except Exception as e: if callback: callback(False, str(e)) self.show_message("保存失败", f"错误: {str(e)}", "error") def close_all(self): """关闭所有对话框""" for dialog in self._active_dialogs.values(): try: dialog.destroy() except: pass self._active_dialogs.clear() # 全局对话框管理器实例 dialog_manager = DialogManager()
08-05
import sys import json import os import warnings from datetime import datetime from PyQt5.QtCore import QUrl, Qt, QDateTime from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLineEdit, QPushButton, QListWidget, QTabWidget, QVBoxLayout, QWidget, QHBoxLayout, QAction, QToolBar, QStatusBar, QProgressBar, QInputDialog, QFileDialog, QMessageBox ) from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings from PyQt5.QtNetwork import QNetworkProxy, QNetworkCookie # 修复代理设置导入问题 # 忽略弃用警告 warnings.filterwarnings("ignore", category=DeprecationWarning) class WebEnginePage(QWebEnginePage): def __init__(self, profile=None, parent=None): """修复参数传递问题:支持带profile和不带profile两种初始化方式""" if profile: super().__init__(profile, parent) # 使用profile初始化 else: super().__init__(parent) # 标准初始化 self.parent_window = parent.parent_window if parent and hasattr(parent, 'parent_window') else None def acceptNavigationRequest(self, url, _type, isMainFrame): # 检测视频流协议并调用播放器 if url.scheme() in ['rtmp', 'http-flv', 'ws-flv']: if self.parent_window: self.parent_window.play_with_jessibuca(url.toString()) return False return super().acceptNavigationRequest(url, _type, isMainFrame) def createWindow(self, type): # 在新标签页打开链接 if type == QWebEnginePage.WebBrowserTab: if self.parent_window: new_tab = self.parent_window.add_browser_tab("加载中...") return new_tab.page return super().createWindow(type) def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID): """修复:正确映射JavaScript控制台日志级别""" # 创建日志级别映射字典 level_map = { QWebEnginePage.InfoMessageLevel: "INFO", QWebEnginePage.WarningMessageLevel: "WARNING", QWebEnginePage.ErrorMessageLevel: "ERROR" } # 获取可读的日志级别名称 level_str = level_map.get(level, "UNKNOWN") log_msg = f"[JS {level_str}] {sourceID}:{lineNumber} - {message}" if self.parent_window: self.parent_window.status_bar.showMessage(log_msg, 5000) print(log_msg) # 同时输出到控制台 class WebEngineView(QWebEngineView): def __init__(self, parent=None): super().__init__(parent) self.parent_window = parent # 修复:正确传递profile参数 self.page = WebEnginePage(profile=self.parent_window.profile, parent=self) self.setPage(self.page) self.page.loadFinished.connect(self.inject_jessibuca) def inject_jessibuca(self): self.page.runJavaScript(""" if (typeof window.Jessibuca === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/jessibuca@latest/dist/jessibuca.js'; script.onerror = () => console.error('Jessibuca加载失败'); document.head.appendChild(script); const container = document.createElement('div'); container.id = 'jessibuca-container'; container.style.position = 'fixed'; container.style.zIndex = '99999'; container.style.top = '0'; container.style.left = '0'; container.style.width = '100%'; container.style.height = '100%'; container.style.backgroundColor = 'black'; container.style.display = 'none'; document.body.appendChild(container); } """) def createWindow(self, type): # 创建新标签页 if type == QWebEnginePage.WebBrowserTab: new_tab = WebEngineView(self.parent_window) # 修复:直接返回page属性而非调用page() return new_tab.page return super().createWindow(type) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("B站多账号浏览器") self.setMinimumSize(1200, 800) # 初始化数据存储 self.account_db = {} self.cookies_db = [] # 创建持久化Cookie配置 self.cookie_storage_path = "cookies_storage" os.makedirs(self.cookie_storage_path, exist_ok=True) self.profile = QWebEngineProfile("BiliCookieProfile", self) self.profile.setPersistentCookiesPolicy(QWebEngineProfile.ForcePersistentCookies) self.profile.setPersistentStoragePath(self.cookie_storage_path) # 关键性能优化1: 全局禁用网络代理[8](@ref) QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.NoProxy)) # 关键性能优化2: 启用HTTP内存缓存[9](@ref) self.profile.setHttpCacheType(QWebEngineProfile.MemoryHttpCache) # 启用HTML5支持的设置 profile_settings = self.profile.settings() profile_settings.setAttribute(QWebEngineSettings.PlaybackRequiresUserGesture, False) profile_settings.setAttribute(QWebEngineSettings.FullScreenSupportEnabled, True) profile_settings.setAttribute(QWebEngineSettings.WebGLEnabled, True) profile_settings.setAttribute(QWebEngineSettings.Accelerated2dCanvasEnabled, True) profile_settings.setAttribute(QWebEngineSettings.PluginsEnabled, True) profile_settings.setAttribute(QWebEngineSettings.AllowRunningInsecureContent, True) # 主界面布局 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 导航工具栏 navbar = QToolBar("导航栏") navbar.setMovable(False) # 禁止工具栏拖动提升性能 self.addToolBar(navbar) back_btn = QAction("←", self) back_btn.triggered.connect(self.navigate_back) navbar.addAction(back_btn) forward_btn = QAction("→", self) forward_btn.triggered.connect(self.navigate_forward) navbar.addAction(forward_btn) reload_btn = QAction("↻", self) reload_btn.triggered.connect(self.reload_page) navbar.addAction(reload_btn) home_btn = QAction("🏠", self) home_btn.triggered.connect(self.navigate_home) navbar.addAction(home_btn) navbar.addSeparator() self.url_bar = QLineEdit("https://www.bilibili.com") self.url_bar.setPlaceholderText("输入网址或搜索内容...") self.url_bar.returnPressed.connect(self.load_url) navbar.addWidget(self.url_bar) # 浏览器标签页 self.browser_tabs = QTabWidget() self.browser_tabs.setTabsClosable(True) self.browser_tabs.tabCloseRequested.connect(self.close_tab) self.browser_tabs.currentChanged.connect(self.update_url_bar) # 关键性能优化3: 启用标签页滚动 self.browser_tabs.setUsesScrollButtons(True) main_layout.addWidget(self.browser_tabs, 8) # 添加初始标签页 self.add_browser_tab("首页", "https://www.bilibili.com") # 管理面板 self.tabs = QTabWidget() self.cookie_list = QListWidget() self.tabs.addTab(self.cookie_list, "Cookie列表") self.account_list = QListWidget() self.tabs.addTab(self.account_list, "账号切换") self.account_list.itemClicked.connect(self.switch_account) main_layout.addWidget(self.tabs, 2) # 功能按钮 btn_layout = QHBoxLayout() self.import_btn = QPushButton("导入Cookie") self.import_btn.clicked.connect(self.import_cookies) self.save_btn = QPushButton("保存账号") self.save_btn.clicked.connect(self.save_account) self.export_btn = QPushButton("导出Cookie") self.export_btn.clicked.connect(self.export_cookies) self.new_tab_btn = QPushButton("新建标签页") self.new_tab_btn.clicked.connect(self.create_new_tab) for btn in [self.import_btn, self.save_btn, self.export_btn, self.new_tab_btn]: btn_layout.addWidget(btn) main_layout.addLayout(btn_layout) # 状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.progress_bar = QProgressBar() self.progress_bar.setMaximum(100) self.progress_bar.setVisible(False) self.progress_bar.setFixedWidth(200) self.status_bar.addPermanentWidget(self.progress_bar) # 加载数据 self.load_accounts() def add_browser_tab(self, title, url=None): browser = WebEngineView(self) browser.titleChanged.connect(lambda title, b=browser: self.update_tab_title(b, title)) browser.loadProgress.connect(self.update_progress) browser.loadFinished.connect(self.on_load_finished) index = self.browser_tabs.addTab(browser, title) self.browser_tabs.setCurrentIndex(index) # 关键性能优化4: 延迟加载提升启动速度 if url: QApplication.processEvents() # 确保UI更新 browser.load(QUrl(url)) return browser def update_tab_title(self, browser, title): index = self.browser_tabs.indexOf(browser) if index != -1: self.browser_tabs.setTabText(index, title[:15] + "..." if len(title) > 15 else title) def update_progress(self, progress): self.progress_bar.setVisible(progress < 100) self.progress_bar.setValue(progress) def close_tab(self, index): """修复资源释放问题:移除多余的括号""" if self.browser_tabs.count() > 1: widget = self.browser_tabs.widget(index) # 关键修复:直接访问page属性而非调用page() page = widget.page if page: # 关键优化: 正确的资源释放顺序 profile = page.profile() profile.cookieStore().deleteAllCookies() # 先删除页面再删除视图 page.deleteLater() widget.deleteLater() self.browser_tabs.removeTab(index) else: self.create_new_tab() def create_new_tab(self): self.add_browser_tab("新标签页", "about:blank") def navigate_back(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.back() def navigate_forward(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.forward() def reload_page(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.reload() def navigate_home(self): self.load_url("https://www.bilibili.com") def on_load_finished(self, success): browser = self.sender() if browser and self.browser_tabs.currentWidget() == browser: current_url = browser.url().toString() self.url_bar.setText(current_url) self.status_bar.showMessage("页面加载完成" if success else "页面加载失败", 2000) # 关键优化: 登录页面自动处理 if "login" in current_url or "passport" in current_url: self.status_bar.showMessage("检测到登录页面,请完成登录", 5000) self.auto_handle_login_page(browser) def auto_handle_login_page(self, browser): """自动处理登录页面的JavaScript""" browser.page.runJavaScript(""" // 尝试自动填充已知的登录表单 const loginForm = document.querySelector('form[action*="login"]'); if (loginForm) { // 尝试填充测试账号 const usernameInput = loginForm.querySelector('input[name="username"], input[name="user"]'); const passwordInput = loginForm.querySelector('input[name="password"]'); if (usernameInput && passwordInput) { usernameInput.value = "test_account"; passwordInput.value = "test_password"; console.log("自动填充了登录表单"); } } """) def update_url_bar(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_url = current_browser.url().toString() self.url_bar.setText(current_url) # 登录页面特殊处理 if "login" in current_url or "passport" in current_url: self.url_bar.setStyleSheet("background-color: #FFF8E1;") else: self.url_bar.setStyleSheet("") def load_url(self, url_text=None): if url_text is None: url_text = self.url_bar.text().strip() if not url_text: return # 关键优化: 更智能的URL处理[10](@ref) if not url_text.startswith(("http://", "https://", "file://", "ftp://")): if "." in url_text: # 包含域名 url_text = "https://" + url_text else: # 可能是搜索内容 url_text = f"https://www.bilibili.com/search?keyword={url_text}" current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.load(QUrl(url_text)) self.status_bar.showMessage(f"正在加载: {url_text}", 3000) def play_with_jessibuca(self, stream_url): """使用Jessibuca播放视频流""" current_browser = self.browser_tabs.currentWidget() if not current_browser: return self.status_bar.showMessage("Jessibuca播放中...", 3000) current_browser.page.runJavaScript(f""" const container = document.getElementById('jessibuca-container'); if (container) {{ container.style.display = 'block'; if (!window.jessibucaPlayer) {{ window.jessibucaPlayer = new Jessibuca({{ container: container, videoBuffer: 0.2, isResize: true, text: '直播加载中...', decoder: 'ffmpeg.js', forceNoOffscreen: true }}); }} window.jessibucaPlayer.play('{stream_url}'); }} """) def import_cookies(self): """导入Cookie JSON文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: with open(file_path, "r", encoding='utf-8') as f: cookies = json.load(f) if isinstance(cookies, list): self.cookies_db = cookies self.cookie_list.clear() self.cookie_list.addItems([f"{c['name']}: {c['value'][:10]}..." for c in cookies]) self.status_bar.showMessage(f"成功导入 {len(cookies)} 个Cookie", 3000) self.inject_cookies(cookies) else: QMessageBox.warning(self, "错误", "无效的Cookie格式") except Exception as e: QMessageBox.critical(self, "导入失败", f"错误: {str(e)}") def export_cookies(self): """导出Cookie到文件""" file_path, _ = QFileDialog.getSaveFileName( self, "保存Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: cookies = self.get_current_cookies() with open(file_path, "w", encoding='utf-8') as f: json.dump(cookies, f, indent=2, ensure_ascii=False) self.status_bar.showMessage("Cookie导出成功", 3000) except Exception as e: QMessageBox.critical(self, "导出失败", f"错误: {str(e)}") def get_current_cookies(self): """获取当前标签页的Cookie(模拟)""" # 实际实现需要异步获取,这里简化处理 return self.cookies_db if self.cookies_db else [] def inject_cookies(self, cookies): """将Cookie注入当前页面 - 修复登录问题[9](@ref)""" current_browser = self.browser_tabs.currentWidget() if current_browser: store = current_browser.page.profile().cookieStore() # 先清除现有Cookie store.deleteAllCookies() # 异步注入新的Cookie for cookie_data in cookies: # 创建Qt的Cookie对象 qt_cookie = QNetworkCookie( cookie_data['name'].encode('utf-8'), cookie_data['value'].encode('utf-8') ) # 设置Cookie属性 if 'domain' in cookie_data: qt_cookie.setDomain(cookie_data['domain']) if 'path' in cookie_data: qt_cookie.setPath(cookie_data['path']) if 'expiry' in cookie_data: # 转换为QDateTime expiry = QDateTime.fromSecsSinceEpoch(cookie_data['expiry']) qt_cookie.setExpirationDate(expiry) # 设置安全属性 qt_cookie.setSecure(cookie_data.get('secure', False)) qt_cookie.setHttpOnly(cookie_data.get('httpOnly', False)) # 注入Cookie store.setCookie(qt_cookie, QUrl(cookie_data.get('url', 'https://www.bilibili.com'))) self.status_bar.showMessage("Cookie注入成功,请刷新页面", 3000) def save_account(self): """保存当前账号配置""" account_name, ok = QInputDialog.getText( self, "保存账号", "输入账号名称:" ) if ok and account_name: cookies = self.get_current_cookies() if cookies: self.account_db[account_name] = { "cookies": cookies, "saved_date": datetime.now().isoformat() } self.account_list.addItem(account_name) # 持久化存储账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) QMessageBox.information(self, "成功", "账号保存成功") except Exception as e: QMessageBox.critical(self, "保存失败", f"账号保存失败: {str(e)}") else: QMessageBox.warning(self, "错误", "没有获取到有效Cookie") def switch_account(self, item): """切换账号""" account_name = item.text() if account_name in self.account_db: cookies = self.account_db[account_name].get("cookies", []) self.inject_cookies(cookies) self.status_bar.showMessage(f"已切换至账号: {account_name}", 3000) # 自动刷新当前页面 self.reload_page() else: QMessageBox.warning(self, "错误", "找不到该账号的Cookie信息") def load_accounts(self): """从文件加载保存的账号""" try: if os.path.exists("accounts.json"): with open("accounts.json", "r", encoding='utf-8') as f: self.account_db = json.load(f) self.account_list.addItems(self.account_db.keys()) except Exception as e: print(f"加载账号失败: {str(e)}") def closeEvent(self, event): """窗口关闭时释放全局资源[8](@ref)""" # 清理HTTP缓存和访问记录 self.profile.clearHttpCache() self.profile.clearAllVisitedLinks() # 保存账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) except Exception as e: print(f"保存账号失败: {str(e)}") super().closeEvent(event) if __name__ == "__main__": # 关键性能优化: 启用WebEngine调试日志[9](@ref) os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging" app = QApplication(sys.argv) # 关键性能优化: 启用Qt内置的OpenGL渲染[7](@ref) app.setAttribute(Qt.AA_UseOpenGLES) window = MainWindow() window.show() sys.exit(app.exec_())
07-09
import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QComboBox, QGroupBox, QSpinBox, QColorDialog, QFileDialog, QTabWidget, QCheckBox, QMessageBox, QSlider, QMenu, QAction, QGridLayout, QScrollArea, QSplitter, QTextEdit, QSizePolicy) from PyQt5.QtGui import QPainter, QPen, QFont, QPixmap, QColor, QMouseEvent, QFontMetrics, QIcon from PyQt5.QtCore import Qt, QSize, QPoint, QRect from PyQt5.QtPrintSupport import QPrinter, QPrintDialog import pyttsx3 # 用于文字朗读功能 # 拼音表数据 PINYIN_TABLE = { "声母": ["b", "p", "m", "f", "d", "t", "n", "l", "g", "k", "h", "j", "q", "x", "zh", "ch", "sh", "r", "z", "c", "s", "y", "w"], "韵母": ["a", "o", "e", "i", "u", "ü", "ai", "ei", "ui", "ao", "ou", "iu", "ie", "üe", "er", "an", "en", "in", "un", "ün", "ang", "eng", "ing", "ong"], "整体认读音节": ["zhi", "chi", "shi", "ri", "zi", "ci", "si", "yi", "wu", "yu", "ye", "yue", "yuan", "yin", "yun", "ying"] } class PaperWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.grid_type = "田字格" self.text = "" self.font_size = 40 self.grid_color = QColor(100, 150, 250) self.text_color = QColor(50, 50, 50) self.background_color = QColor(255, 255, 255) self.grid_size = 60 self.margin = 40 self.rows = 10 self.cols = 8 self.show_text = False self.show_pinyin = False # 新增:是否显示拼音 self.show_radical = False # 新增:是否显示偏旁部首 self.grid_offset = QPoint(0, 0) # 新增:网格偏移量 self.text_offset = QPoint(0, 0) # 新增:文字偏移量 self.char_rects = [] # 新增:存储每个字符的位置信息 self.tts_engine = None # 延迟初始化语音引擎 def init_tts_engine(self): """延迟初始化语音引擎""" if self.tts_engine is None: try: self.tts_engine = pyttsx3.init() except Exception as e: print(f"初始化语音引擎失败: {e}") self.tts_engine = None def set_grid_type(self, grid_type): self.grid_type = grid_type self.update() def set_text(self, text): self.text = text self.update() def set_font_size(self, size): self.font_size = size self.update() def set_grid_color(self, color): self.grid_color = color self.update() def set_text_color(self, color): self.text_color = color self.update() def set_background_color(self, color): self.background_color = color self.update() def set_grid_size(self, size): self.grid_size = size self.update() def set_rows(self, rows): self.rows = rows self.update() def set_cols(self, cols): self.cols = cols self.update() def set_show_text(self, show): self.show_text = show self.update() def set_show_pinyin(self, show): self.show_pinyin = show self.update() def set_show_radical(self, show): self.show_radical = show self.update() def set_grid_offset(self, dx, dy): self.grid_offset.setX(dx) self.grid_offset.setY(dy) self.update() def set_text_offset(self, dx, dy): self.text_offset.setX(dx) self.text_offset.setY(dy) self.update() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 绘制背景 painter.fillRect(self.rect(), self.background_color) # 计算起始位置,使网格居中,并应用偏移量 total_width = self.cols * self.grid_size total_height = self.rows * self.grid_size start_x = (self.width() - total_width) // 2 + self.grid_offset.x() start_y = (self.height() - total_height) // 2 + self.grid_offset.y() # 绘制网格 pen = QPen(self.grid_color) pen.setWidth(1) painter.setPen(pen) if self.grid_type == "田字格": self.draw_tian_grid(painter, start_x, start_y) elif self.grid_type == "米字格": self.draw_mi_grid(painter, start_x, start_y) elif self.grid_type == "四线格": self.draw_si_grid(painter, start_x, start_y) elif self.grid_type == "方格": self.draw_square_grid(painter, start_x, start_y) elif self.grid_type == "拼音格": self.draw_pinyin_grid(painter, start_x, start_y) # 绘制文字和拼音(仅在show_text为True时) if self.show_text and self.text: self.draw_text_and_pinyin(painter, start_x, start_y) def draw_tian_grid(self, painter, start_x, start_y): # 绘制外框和内部十字 for row in range(self.rows): for col in range(self.cols): x = start_x + col * self.grid_size y = start_y + row * self.grid_size # 绘制外框 painter.drawRect(x, y, self.grid_size, self.grid_size) # 绘制十字 painter.drawLine(x, y + self.grid_size // 2, x + self.grid_size, y + self.grid_size // 2) painter.drawLine(x + self.grid_size // 2, y, x + self.grid_size // 2, y + self.grid_size) def draw_mi_grid(self, painter, start_x, start_y): # 绘制外框和米字 for row in range(self.rows): for col in range(self.cols): x = start_x + col * self.grid_size y = start_y + row * self.grid_size # 绘制外框 painter.drawRect(x, y, self.grid_size, self.grid_size) # 绘制十字 painter.drawLine(x, y + self.grid_size // 2, x + self.grid_size, y + self.grid_size // 2) painter.drawLine(x + self.grid_size // 2, y, x + self.grid_size // 2, y + self.grid_size) # 绘制对角线 painter.drawLine(x, y, x + self.grid_size, y + self.grid_size) painter.drawLine(x + self.grid_size, y, x, y + self.grid_size) def draw_si_grid(self, painter, start_x, start_y): # 绘制四线格 for row in range(self.rows): for col in range(self.cols): x = start_x + col * self.grid_size y = start_y + row * self.grid_size # 绘制四条横线 line_spacing = self.grid_size // 3 for i in range(4): painter.drawLine(x, y + i * line_spacing, x + self.grid_size, y + i * line_spacing) def draw_square_grid(self, painter, start_x, start_y): # 绘制方格 for row in range(self.rows): for col in range(self.cols): x = start_x + col * self.grid_size y = start_y + row * self.grid_size # 绘制外框 painter.drawRect(x, y, self.grid_size, self.grid_size) def draw_pinyin_grid(self, painter, start_x, start_y): # 绘制拼音格(上半部分四线格,下半部分田字格) for row in range(self.rows): for col in range(self.cols): x = start_x + col * self.grid_size y = start_y + row * self.grid_size # 绘制上半部分的四线格 line_spacing = self.grid_size // 6 for i in range(4): painter.drawLine(x, y + i * line_spacing, x + self.grid_size, y + i * line_spacing) # 绘制下半部分的田字格 bottom_y = y + self.grid_size // 2 painter.drawRect(x, bottom_y, self.grid_size, self.grid_size // 2) painter.drawLine(x, bottom_y + self.grid_size // 4, x + self.grid_size, bottom_y + self.grid_size // 4) painter.drawLine(x + self.grid_size // 2, bottom_y, x + self.grid_size // 2, bottom_y + self.grid_size // 2) def draw_text_and_pinyin(self, painter, start_x, start_y): self.char_rects = [] # 重置字符位置信息 # 设置文字字体 text_font = QFont("楷体", self.font_size) painter.setFont(text_font) painter.setPen(self.text_color) # 设置拼音字体(稍小) pinyin_font = QFont("Arial", self.font_size // 3) # 设置偏旁部首字体(稍小) radical_font = QFont("楷体", self.font_size // 3) # 将文本拆分为字符 chars = list(self.text) char_index = 0 # 在每个格子中心绘制文字和拼音 for row in range(self.rows): for col in range(self.cols): if char_index >= len(chars): return char = chars[char_index] x = start_x + col * self.grid_size + self.grid_size // 2 y = start_y + row * self.grid_size + self.grid_size // 2 # 调整文字位置 - 居中并稍微偏下 y_offset = 5 # 向下偏移5像素,使文字更居中 if self.grid_type == "拼音格": y = start_y + row * self.grid_size + self.grid_size // 4 + y_offset else: y = start_y + row * self.grid_size + self.grid_size // 2 + y_offset # 应用文字偏移 x += self.text_offset.x() y += self.text_offset.y() # 绘制拼音(如果启用) pinyin_rect = QRect() # 初始化pinyin_rect if self.show_pinyin and char in HANZI_DATA: painter.setFont(pinyin_font) pinyin = HANZI_DATA[char]["pinyin"] pinyin_rect = painter.fontMetrics().boundingRect(pinyin) painter.drawText(x - pinyin_rect.width() // 2, y - self.font_size // 2 - 5, # 拼音在文字上方 pinyin) painter.setFont(text_font) # 绘制偏旁部首(如果启用) radical_rect = QRect() # 初始化radical_rect if self.show_radical and char in HANZI_DATA: painter.setFont(radical_font) radical = HANZI_DATA[char]["radical"] radical_rect = painter.fontMetrics().boundingRect(radical) # 将偏旁部首绘制在文字的右上角 painter.drawText(x + self.font_size // 4, y - self.font_size // 4, radical) painter.setFont(text_font) # 居中绘制文字 text_rect = painter.fontMetrics().boundingRect(char) painter.drawText(x - text_rect.width() // 2, y + text_rect.height() // 4, char) # 存储字符位置信息(用于点击事件) char_rect = QRect( x - text_rect.width() // 2, y - (self.font_size // 2 if self.show_pinyin else 0), max(text_rect.width(), pinyin_rect.width() if self.show_pinyin and char in HANZI_DATA else 0), text_rect.height() + (self.font_size // 2 if self.show_pinyin and char in HANZI_DATA else 0) ) self.char_rects.append((char, char_rect)) char_index += 1 def mousePressEvent(self, event: QMouseEvent): if not self.show_text or not self.text: return # 检查是否点击了某个字符 for char, rect in self.char_rects: if rect.contains(event.pos()): self.show_character_info(char) break def show_character_info(self, char): if char in HANZI_DATA: data = HANZI_DATA[char] info = f"字符: {char}\n拼音: {data['pinyin']}\n偏旁部首: {data['radical']}\n解释: {data['explanation']}" QMessageBox.information(self, "字符信息", info) # 朗读字符和拼音 try: self.init_tts_engine() if self.tts_engine: self.tts_engine.say(char) self.tts_engine.say(data['pinyin']) self.tts_engine.runAndWait() except Exception as e: print(f"朗读出错: {e}") else: QMessageBox.information(self, "字符信息", f"字符 '{char}' 的信息未找到") def sizeHint(self): return QSize(600, 800) class PinyinTableWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.tts_engine = None # 延迟初始化语音引擎 self.init_ui() def init_tts_engine(self): """延迟初始化语音引擎""" if self.tts_engine is None: try: self.tts_engine = pyttsx3.init() except Exception as e: print(f"初始化语音引擎失败: {e}") self.tts_engine = None def init_ui(self): # 创建选项卡显示不同类型的拼音 self.tabs = QTabWidget() # 声母表 initial_tab = QWidget() self.setup_pinyin_grid(initial_tab, PINYIN_TABLE["声母"], "声母") self.tabs.addTab(initial_tab, "声母") # 韵母表 final_tab = QWidget() self.setup_pinyin_grid(final_tab, PINYIN_TABLE["韵母"], "韵母") self.tabs.addTab(final_tab, "韵母") # 整体认读音节表 syllable_tab = QWidget() self.setup_pinyin_grid(syllable_tab, PINYIN_TABLE["整体认读音节"], "整体认读音节") self.tabs.addTab(syllable_tab, "整体认读音节") # 设置主布局 main_layout = QVBoxLayout(self) main_layout.addWidget(self.tabs) def setup_pinyin_grid(self, tab, pinyin_list, title): # 创建滚动区域 scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) # 创建容器部件 container = QWidget() grid_layout = QGridLayout(container) # 添加拼音按钮 row, col = 0, 0 for pinyin in pinyin_list: btn = QPushButton(pinyin) btn.setFixedSize(60, 60) btn.setStyleSheet(""" QPushButton { font-size: 18px; font-weight: bold; background-color: #89c2ff; border: none; border-radius: 8px; color: white; } QPushButton:hover { background-color: #5fa3ff; } """) btn.clicked.connect(lambda checked, p=pinyin: self.play_pinyin(p)) grid_layout.addWidget(btn, row, col) col += 1 if col > 4: # 每行5个按钮 col = 0 row += 1 # 设置滚动区域的内容 scroll_area.setWidget(container) # 设置选项卡布局 tab_layout = QVBoxLayout(tab) tab_layout.addWidget(QLabel(f"{title}表")) tab_layout.addWidget(scroll_area) def play_pinyin(self, pinyin): # 播放拼音 try: self.init_tts_engine() if self.tts_engine: self.tts_engine.say(pinyin) self.tts_engine.runAndWait() except Exception as e: print(f"朗读出错: {e}") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("儿童字格生成器") self.setGeometry(100, 100, 1200, 800) # 设置可爱的样式 self.setStyleSheet(""" QMainWindow { background-color: #f0f9ff; } QGroupBox { font-weight: bold; border: 2px solid #89c2ff; border-radius: 8px; margin-top: 1ex; background-color: #e6f3ff; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; color: #2b6cb0; } QPushButton { background-color: #89c2ff; border: none; border-radius: 8px; color: white; padding: 8px; font-weight: bold; } QPushButton:hover { background-color: #5fa3ff; } QPushButton:pressed { background-color: #3b82f6; } QLabel { color: #2b6cb0; font-weight: bold; } QLineEdit { border: 2px solid #89c2ff; border-radius: 5px; padding: 5px; background-color: white; } QComboBox, QSpinBox { border: 2px solid #89c2ff; border-radius: 5px; padding: 5px; background-color: white; } QTabWidget::pane { border: 2px solid #89c2ff; border-radius: 8px; background-color: #e6f3ff; } QTabBar::tab { background-color: #c7e0ff; border: 2px solid #89c2ff; border-bottom: none; border-top-left-radius: 8px; border-top-right-radius: 8px; padding: 8px; margin-right: 2px; } QTabBar::tab:selected { background-color: #89c2ff; color: white; } QSlider::groove:horizontal { border: 1px solid #89c2ff; height: 8px; background: #e6f3ff; border-radius: 4px; } QSlider::handle:horizontal { background: #89c2ff; border: 1px solid #5fa3ff; width: 18px; margin: -5px 0; border-radius: 9px; } """) # 创建中央部件和布局 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QHBoxLayout(central_widget) # 创建选项卡 self.tabs = QTabWidget() main_layout.addWidget(self.tabs) # 创建空白模版选项卡 self.blank_template_tab = QWidget() self.setup_blank_template_tab() self.tabs.addTab(self.blank_template_tab, "空白字格模版") # 创建带文字模版选项卡 self.text_template_tab = QWidget() self.setup_text_template_tab() self.tabs.addTab(self.text_template_tab, "带文字字格模版") # 创建文字朗读和拼音设置模版 self.tts_pinyin_tab = QWidget() self.setup_tts_pinyin_tab() self.tabs.addTab(self.tts_pinyin_tab, "文字朗读和拼音设置") # 创建拼音表选项卡 self.pinyin_table_tab = QWidget() self.setup_pinyin_table_tab() self.tabs.addTab(self.pinyin_table_tab, "拼音表") # 创建纸张显示部件 self.paper_widget = PaperWidget() main_layout.addWidget(self.paper_widget, 2) def setup_blank_template_tab(self): layout = QVBoxLayout(self.blank_template_tab) # 字格类型选择 grid_type_layout = QHBoxLayout() grid_type_layout.addWidget(QLabel("字格类型:")) self.grid_type_combo = QComboBox() self.grid_type_combo.addItems(["田字格", "米字格", "四线格", "方格", "拼音格"]) self.grid_type_combo.currentTextChanged.connect(self.update_grid_type) grid_type_layout.addWidget(self.grid_type_combo) layout.addLayout(grid_type_layout) # 网格尺寸设置 grid_size_group = QGroupBox("网格尺寸设置") grid_size_layout = QVBoxLayout(grid_size_group) # 网格大小 size_layout = QHBoxLayout() size_layout.addWidget(QLabel("网格大小:")) self.grid_size_spin = QSpinBox() self.grid_size_spin.setRange(20, 100) self.grid_size_spin.setValue(60) self.grid_size_spin.valueChanged.connect(self.update_grid_size) size_layout.addWidget(self.grid_size_spin) grid_size_layout.addLayout(size_layout) # 网格行数 rows_layout = QHBoxLayout() rows_layout.addWidget(QLabel("行数:")) self.rows_spin = QSpinBox() self.rows_spin.setRange(1, 20) self.rows_spin.setValue(10) self.rows_spin.valueChanged.connect(self.update_rows) rows_layout.addWidget(self.rows_spin) grid_size_layout.addLayout(rows_layout) # 网格列数 cols_layout = QHBoxLayout() cols_layout.addWidget(QLabel("列数:")) self.cols_spin = QSpinBox() self.cols_spin.setRange(1, 20) self.cols_spin.setValue(8) self.cols_spin.valueChanged.connect(self.update_cols) cols_layout.addWidget(self.cols_spin) grid_size_layout.addLayout(cols_layout) layout.addWidget(grid_size_group) # 颜色选择 grid_color_btn = QPushButton("选择网格颜色") grid_color_btn.clicked.connect(self.choose_grid_color) layout.addWidget(grid_color_btn) bg_color_btn = QPushButton("选择背景颜色") bg_color_btn.clicked.connect(self.choose_bg_color) layout.addWidget(bg_color_btn) # 功能按钮 print_btn = QPushButton("打印") print_btn.clicked.connect(self.print_paper) layout.addWidget(print_btn) save_btn = QPushButton("保存为图片") save_btn.clicked.connect(self.save_as_image) layout.addWidget(save_btn) # 添加弹性空间 layout.addStretch() def setup_text_template_tab(self): layout = QVBoxLayout(self.text_template_tab) # 字格类型选择和显示文字选项 grid_type_layout = QHBoxLayout() grid_type_layout.addWidget(QLabel("字格类型:")) self.text_grid_type_combo = QComboBox() self.text_grid_type_combo.addItems(["田字格", "米字格", "四线格", "方格", "拼音格"]) self.text_grid_type_combo.currentTextChanged.connect(self.update_grid_type) grid_type_layout.addWidget(self.text_grid_type_combo) # 显示文字复选框 self.show_text_checkbox = QCheckBox("显示文字") self.show_text_checkbox.setChecked(True) self.show_text_checkbox.stateChanged.connect(self.update_show_text) grid_type_layout.addWidget(self.show_text_checkbox) layout.addLayout(grid_type_layout) # 文字输入设置 text_group = QGroupBox("文字输入设置") text_layout = QVBoxLayout(text_group) # 文字输入 input_layout = QHBoxLayout() input_layout.addWidget(QLabel("输入文字:")) self.text_input = QLineEdit() self.text_input.textChanged.connect(self.update_text) input_layout.addWidget(self.text_input) text_layout.addLayout(input_layout) layout.addWidget(text_group) # 文字位置调整 text_position_group = QGroupBox("文字位置调整") text_position_layout = QVBoxLayout(text_position_group) # 水平位置调整 text_h_layout = QHBoxLayout() text_h_layout.addWidget(QLabel("左右:")) self.text_h_slider = QSlider(Qt.Horizontal) self.text_h_slider.setRange(-50, 50) self.text_h_slider.setValue(0) self.text_h_slider.valueChanged.connect(self.update_text_position) text_h_layout.addWidget(self.text_h_slider) text_position_layout.addLayout(text_h_layout) # 垂直位置调整 text_v_layout = QHBoxLayout() text_v_layout.addWidget(QLabel("上下:")) self.text_v_slider = QSlider(Qt.Horizontal) self.text_v_slider.setRange(-50, 50) self.text_v_slider.setValue(0) self.text_v_slider.valueChanged.connect(self.update_text_position) text_v_layout.addWidget(self.text_v_slider) text_position_layout.addLayout(text_v_layout) # 重置文字位置按钮 reset_text_btn = QPushButton("重置文字位置") reset_text_btn.clicked.connect(self.reset_text_position) text_position_layout.addWidget(reset_text_btn) layout.addWidget(text_position_group) # 字体设置 font_group = QGroupBox("字体设置") font_layout = QVBoxLayout(font_group) # 字体大小 font_size_layout = QHBoxLayout() font_size_layout.addWidget(QLabel("字体大小:")) self.font_size_spin = QSpinBox() self.font_size_spin.setRange(10, 100) self.font_size_spin.setValue(40) self.font_size_spin.valueChanged.connect(self.update_font_size) font_size_layout.addWidget(self.font_size_spin) font_layout.addLayout(font_size_layout) layout.addWidget(font_group) # 网格尺寸设置 grid_size_group = QGroupBox("网格尺寸设置") grid_size_layout = QVBoxLayout(grid_size_group) # 网格大小 size_layout = QHBoxLayout() size_layout.addWidget(QLabel("网格大小:")) self.text_grid_size_spin = QSpinBox() self.text_grid_size_spin.setRange(20, 100) self.text_grid_size_spin.setValue(60) self.text_grid_size_spin.valueChanged.connect(self.update_grid_size) size_layout.addWidget(self.text_grid_size_spin) grid_size_layout.addLayout(size_layout) # 网格行数 rows_layout = QHBoxLayout() rows_layout.addWidget(QLabel("行数:")) self.text_rows_spin = QSpinBox() self.text_rows_spin.setRange(1, 20) self.text_rows_spin.setValue(10) self.text_rows_spin.valueChanged.connect(self.update_rows) rows_layout.addWidget(self.text_rows_spin) grid_size_layout.addLayout(rows_layout) # 网格列数 cols_layout = QHBoxLayout() cols_layout.addWidget(QLabel("列数:")) self.text_cols_spin = QSpinBox() self.text_cols_spin.setRange(1, 20) self.text_cols_spin.setValue(8) self.text_cols_spin.valueChanged.connect(self.update_cols) cols_layout.addWidget(self.text_cols_spin) grid_size_layout.addLayout(cols_layout) layout.addWidget(grid_size_group) # 颜色选择 grid_color_btn = QPushButton("选择网格颜色") grid_color_btn.clicked.connect(self.choose_grid_color) layout.addWidget(grid_color_btn) text_color_btn = QPushButton("选择文字颜色") text_color_btn.clicked.connect(self.choose_text_color) layout.addWidget(text_color_btn) bg_color_btn = QPushButton("选择背景颜色") bg_color_btn.clicked.connect(self.choose_bg_color) layout.addWidget(bg_color_btn) # 功能按钮 print_btn = QPushButton("打印") print_btn.clicked.connect(self.print_paper) layout.addWidget(print_btn) save_btn = QPushButton("保存为图片") save_btn.clicked.connect(self.save_as_image) layout.addWidget(save_btn) clear_btn = QPushButton("清空文字") clear_btn.clicked.connect(self.clear_text) layout.addWidget(clear_btn) # 添加弹性空间 layout.addStretch() def setup_tts_pinyin_tab(self): layout = QVBoxLayout(self.tts_pinyin_tab) # 朗读设置 tts_group = QGroupBox("朗读设置") tts_layout = QVBoxLayout(tts_group) # 文字输入和朗读按钮 tts_input_layout = QHBoxLayout() tts_input_layout.addWidget(QLabel("输入文字:")) self.tts_text_input = QLineEdit() tts_input_layout.addWidget(self.tts_text_input) # 喇叭图标按钮 self.speak_button = QPushButton() self.speak_button.setIcon(QIcon.fromTheme("audio-volume-medium")) self.speak_button.setToolTip("朗读文字和拼音") self.speak_button.clicked.connect(self.speak_text_with_pinyin) tts_input_layout.addWidget(self.speak_button) tts_layout.addLayout(tts_input_layout) # 测试按钮 tts_test_btn = QPushButton("测试朗读功能") tts_test_btn.clicked.connect(self.test_tts) tts_layout.addWidget(tts_test_btn) # 音量设置 tts_volume_layout = QHBoxLayout() tts_volume_layout.addWidget(QLabel("音量:")) self.tts_volume_slider = QSlider(Qt.Horizontal) self.tts_volume_slider.setRange(0, 100) self.tts_volume_slider.setValue(70) self.tts_volume_slider.valueChanged.connect(self.update_tts_volume) tts_volume_layout.addWidget(self.tts_volume_slider) tts_layout.addLayout(tts_volume_layout) # 语速设置 tts_rate_layout = QHBoxLayout() tts_rate_layout.addWidget(QLabel("语速:")) self.tts_rate_slider = QSlider(Qt.Horizontal) self.tts_rate_slider.setRange(0, 200) self.tts_rate_slider.setValue(130) self.tts_rate_slider.valueChanged.connect(self.update_tts_rate) tts_rate_layout.addWidget(self.tts_rate_slider) tts_layout.addLayout(tts_rate_layout) layout.addWidget(tts_group) # 拼音设置 pinyin_group = QGroupBox("拼音设置") pinyin_layout = QVBoxLayout(pinyin_group) self.show_pinyin_checkbox_tts = QCheckBox("显示拼音") self.show_pinyin_checkbox_tts.stateChanged.connect(self.update_show_pinyin) pinyin_layout.addWidget(self.show_pinyin_checkbox_tts) self.show_radical_checkbox_tts = QCheckBox("显示偏旁部首") self.show_radical_checkbox_tts.stateChanged.connect(self.update_show_radical) pinyin_layout.addWidget(self.show_radical_checkbox_tts) layout.addWidget(pinyin_group) # 添加弹性空间 layout.addStretch() def setup_pinyin_table_tab(self): layout = QVBoxLayout(self.pinyin_table_tab) # 创建拼音表部件 self.pinyin_table_widget = PinyinTableWidget() layout.addWidget(self.pinyin_table_widget) def speak_text_with_pinyin(self): text = self.tts_text_input.text() if text: # 更新字格显示 self.paper_widget.set_text(text) self.paper_widget.set_show_text(True) self.show_text_checkbox.setChecked(True) # 构建朗读字符串(文字和拼音) speak_text = "" for char in text: speak_text += char + " " if char in HANZI_DATA: speak_text += HANZI_DATA[char]['pinyin'] + " " # 朗读文字和拼音 try: self.paper_widget.init_tts_engine() if self.paper_widget.tts_engine: self.paper_widget.tts_engine.say(speak_text) self.paper_widget.tts_engine.runAndWait() except Exception as e: QMessageBox.warning(self, "朗读错误", f"朗读功能出错: {e}") def test_tts(self): try: self.paper_widget.init_tts_engine() if self.paper_widget.tts_engine: self.paper_widget.tts_engine.say("测试朗读功能") self.paper_widget.tts_engine.runAndWait() except Exception as e: QMessageBox.warning(self, "朗读错误", f"朗读功能出错: {e}") def update_tts_volume(self, volume): self.paper_widget.init_tts_engine() if self.paper_widget.tts_engine: self.paper_widget.tts_engine.setProperty('volume', volume / 100) def update_tts_rate(self, rate): self.paper_widget.init_tts_engine() if self.paper_widget.tts_engine: self.paper_widget.tts_engine.setProperty('rate', rate) def update_show_pinyin(self, state): self.paper_widget.set_show_pinyin(state == Qt.Checked) def update_show_radical(self, state): self.paper_widget.set_show_radical(state == Qt.Checked) def update_text_position(self): dx = self.text_h_slider.value() dy = self.text_v_slider.value() self.paper_widget.set_text_offset(dx, dy) def reset_text_position(self): self.text_h_slider.setValue(0) self.text_v_slider.setValue(0) self.paper_widget.set_text_offset(0, 0) def update_rows(self, rows): self.paper_widget.set_rows(rows) def update_cols(self, cols): self.paper_widget.set_cols(cols) def update_grid_type(self, grid_type): self.paper_widget.set_grid_type(grid_type) def update_text(self, text): self.paper_widget.set_text(text) def update_font_size(self, size): self.paper_widget.set_font_size(size) def update_grid_size(self, size): self.paper_widget.set_grid_size(size) def update_show_text(self, state): self.paper_widget.set_show_text(state == Qt.Checked) def choose_grid_color(self): color = QColorDialog.getColor() if color.isValid(): self.paper_widget.set_grid_color(color) def choose_text_color(self): color = QColorDialog.getColor() if color.isValid(): self.paper_widget.set_text_color(color) def choose_bg_color(self): color = QColorDialog.getColor() if color.isValid(): self.paper_widget.set_background_color(color) def print_paper(self): printer = QPrinter(QPrinter.HighResolution) printer.setPageSize(QPrinter.A4) printer.setOrientation(QPrinter.Portrait) print_dialog = QPrintDialog(printer, self) if print_dialog.exec_() == QPrintDialog.Accepted: painter = QPainter(printer) rect = printer.pageRect() self.paper_widget.render(painter) painter.end() def save_as_image(self): file_path, _ = QFileDialog.getSaveFileName( self, "保存图片", "", "PNG图片 (*.png);;JPEG图片 (*.jpg *.jpeg)" ) if file_path: pixmap = QPixmap(self.paper_widget.size()) self.paper_widget.render(pixmap) pixmap.save(file_path) def clear_text(self): self.text_input.clear() self.paper_widget.set_text("") if __name__ == "__main__": app = QApplication(sys.argv) # 设置应用程序字体 font = QFont("微软雅黑", 10) app.setFont(font) window = MainWindow() window.show() sys.exit(app.exec_()) 1.修改“拼音格”模版高度,高度要加大,格式不能变形。要和米字格一样的大小。2.重新输入文字以后,重新语音朗读出来。3.删除朗读和拼音设置中的拼音设置功能。
最新发布
08-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值