小说摸鱼阅读器 2.0

查看 92|回复 10
作者:phantomxjc   
本来我只是想做一个小说阅读器的,不过看了一下评论很多的友友注重摸鱼去了,那我就根据评论需求优化一下:(这篇文字是AI写的,偷懒哈哈哈哈)1. 核心功能概览这款小说阅读器旨在为用户提供一站式的阅读解决方案,其主要功能模块如下:功能模块               主要特点小说搜索        支持在线搜索海量小说资源章节导航        清晰的章节列表和快速跳转阅读界面        可自定义字体、背景色、支持极简模式文件管理        支持本地TXT文件阅读和管理个性化设置      字体调节、背景色选择、窗口透明度调整2. 在线阅读功能详解2.1 智能搜索与发现应用程序内置了强大的搜索引擎,可以从多个网络源搜索小说资源。用户只需输入小说名称或作者,即可获取丰富的搜索结果列表。搜索过程在后台线程中进行,不会阻塞界面操作。2.2 章节列表与导航选定小说后,应用会自动获取并展示清晰的章节列表。每个章节都支持点击跳转,方便快速定位到想阅读的内容。这一设计避免了传统阅读器需要手动查找章节的麻烦。3. 阅读界面设计与交互优化3.1 个性化阅读设置阅读器提供了丰富的个性化设置选项,包括:• 字体调节:支持字体大小、字体家族的实时调整• 背景色选择:可自定义背景颜色,减少长时间阅读的视觉疲劳• 界面布局:可调节的界面元素和布局,适应不同阅读习惯3.2 极简阅读模式ctrl+f调出极简模式,极简模式是这款阅读器的一大亮点,它提供了无边框、可调节透明度的阅读环境。在此模式下:• 界面元素最大化隐藏,只保留纯文本内容• 支持窗口透明度调节,方便多任务操作• 可通过鼠标拖动调整窗口位置和大小• 使用鼠标滚轮或键盘快捷键进行滚动3.3 智能翻页与进度管理阅读器会自动记录阅读进度,并提供上一章/下一章的便捷导航功能。同时,书签和进度保存功能确保用户可以随时中断和继续阅读。4. 本地文件管理功能除了在线阅读,该应用还提供了强大的本地文件管理能力:4.1 多功能文件操作通过集成的多功能按钮,用户可以:• 右键点击选择并保存本地文件路径• 左键点击打开已保存的本地文件直接阅读• 文件路径会持久化保存,方便下次快速访问结语这款基于Python开发的小说阅读器,结合了现代阅读软件的诸多优点,提供了高度可定制的阅读体验。无论是作为个人项目学习GUI开发,还是作为日常阅读工具,都具有很高的实用价值。 注:本项目仅供学习和交流使用,请尊重版权,合法获取阅读资源。
[Python] 纯文本查看 复制代码import sys
import os
import time
from urllib.parse import quote
import requests
import re
import json
from bs4 import BeautifulSoup
from requests import Session
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QLineEdit, QPushButton,
                             QListWidget, QTextEdit, QStackedWidget, QFrame,
                             QScrollArea, QScrollBar, QProgressBar, QMessageBox,
                             QDialog, QTextBrowser, QFileDialog, QSplitter, QInputDialog, QColorDialog, QFontDialog)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QPropertyAnimation, QEasingCurve, QRect, QPoint
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QCursor, QMouseEvent
# 爬虫部分(保持不变)
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
}
session = Session()
def session_ask():
    try:
        url = 'http://m.xqishuta.net'
        session.get(url=url, headers=headers, timeout=30)
    except Exception as e:
        print(f"Session初始化失败: {e}")
def novel(name):
    novels_list = []
    try:
        url = 'http://m.xqishuta.net/search.html'
        data = {"searchkey": name, "type": "articlename"}
        response = session.post(url=url, headers=headers, data=data, timeout=30)
        htmls = BeautifulSoup(response.text, 'html.parser').find('div', class_='searchresult').find_all('p')
        for i, html in enumerate(htmls, 1):
            novel_name = html.a.text
            href = 'http://m.xqishuta.net' + html.a['href']
            author = html.span.text
            novel_list = [i, novel_name, author, href]
            novels_list.append(novel_list)
    except Exception as e:
        print(f"搜索小说失败: {e}")
    return novels_list
def single_novel(url):
    chapters_list = []
    try:
        response = session.get(url=url, headers=headers)
        response.encoding = 'utf-8'
        options = BeautifulSoup(response.text, 'html.parser').find('select').find_all('option')
        i = 1
        for option in options:
            value = 'http://m.xqishuta.net/' + option.get('value')
            response = session.get(url=value, headers=headers)
            response.encoding = 'utf-8'
            chapters = BeautifulSoup(response.text, 'html.parser').find_all('div', class_='info_menu1')
            if len(chapters) > 1:
                chapters = chapters[1].find('div', class_='list_xm').find_all('li')
                for chapter in chapters:
                    chapter_name = chapter.a.text
                    url = 'http://m.xqishuta.net' + chapter.a['href']
                    chapter_list = [i, chapter_name, url]
                    chapters_list.append(chapter_list)
                    i += 1
    except Exception as e:
        print(f"获取章节列表失败: {e}")
    return chapters_list
def text(url):
    all_text = ""
    try:
        while True:
            response = session.get(url=url, headers=headers)
            response.encoding = 'utf-8'
            all_text += clear_text(response)
            soup = BeautifulSoup(response.text, 'html.parser').find('p', class_='p1 p3')
            if soup and soup.text == '下一页' and soup.a:
                url = 'http://m.xqishuta.net' + soup.a['href']
            else:
                break
    except Exception as e:
        print(f"获取小说内容失败: {e}")
        all_text = f"获取内容失败: {e}"
    return all_text
def clear_text(response):
    try:
        soup = BeautifulSoup(response.text, 'html.parser').find('div', class_='novelcontent').find('p')
        text = str(soup.text)
        text = re.sub(r'最新网址:\S*\s*', '', text)
        text = re.sub(r'第[^章]+章\s*[^(]*\s*\(第\d+/\d+页\)\s*', '', text)
        text = re.sub(r'(本章未完,请点击下一页继续阅读)', '', text)
        return text
    except Exception as e:
        return f"内容解析失败: {e}"
# 多线程处理网络请求
class SearchThread(QThread):
    finished = pyqtSignal(list)
    error = pyqtSignal(str)
    def __init__(self, novel_name):
        super().__init__()
        self.novel_name = novel_name
    def run(self):
        try:
            result = novel(self.novel_name)
            self.finished.emit(result)
        except Exception as e:
            self.error.emit(str(e))
class ChapterThread(QThread):
    finished = pyqtSignal(list)
    error = pyqtSignal(str)
    def __init__(self, url):
        super().__init__()
        self.url = url
    def run(self):
        try:
            result = single_novel(self.url)
            self.finished.emit(result)
        except Exception as e:
            self.error.emit(str(e))
class ContentThread(QThread):
    finished = pyqtSignal(str)
    error = pyqtSignal(str)
    def __init__(self, url):
        super().__init__()
        self.url = url
    def run(self):
        try:
            result = text(self.url)
            self.finished.emit(result)
        except Exception as e:
            self.error.emit(str(e))
# 自定义多功能按钮类
class MultiFunctionButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.saved_file_path = ""  # 保存的文件路径
        self.config_file = "saved_file_path.txt"  # 配置文件路径
        # 尝试读取之前保存的文件路径
        self.load_saved_path()
    def load_saved_path(self):
        """加载之前保存的文件路径"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as file:
                    self.saved_file_path = file.read().strip()
        except Exception as e:
            print(f"加载保存的文件路径失败: {e}")
    def mousePressEvent(self, event):
        if event.button() == Qt.RightButton:
            # 右键:浏览文件并保存路径
            self.browse_and_save_file()
        else:
            # 左键:打开保存的文件
            super().mousePressEvent(event)
            self.open_saved_file()
    def browse_and_save_file(self):
        """右键功能:浏览文件并保存路径到txt"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择文件", "", "所有文件 (*.*)"
        )
        if file_path:
            try:
                # 保存文件路径到txt文件
                with open(self.config_file, 'w', encoding='utf-8') as file:
                    file.write(file_path)
                self.saved_file_path = file_path
                QMessageBox.information(self, "成功", f"文件路径已保存: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存文件路径失败: {str(e)}")
    def open_saved_file(self):
        """左键功能:打开保存的文件"""
        if not self.saved_file_path or not os.path.exists(self.saved_file_path):
            QMessageBox.warning(self, "提示", "请先右键点击按钮选择文件")
            return
        try:
            # 读取文件内容
            with open(self.saved_file_path, 'r', encoding='utf-8') as file:
                content = file.read()
            # 获取父组件(ReadingWidget)并显示文件内容
            parent_widget = self.parent()
            while parent_widget and not isinstance(parent_widget, ReadingWidget):
                parent_widget = parent_widget.parent()
            if parent_widget and hasattr(parent_widget, 'content_text'):
                parent_widget.content_text.setPlainText(content)
                parent_widget.novel_title_label.setText(f"本地文件: {os.path.basename(self.saved_file_path)}")
                QMessageBox.information(self, "成功", f"已打开文件: {os.path.basename(self.saved_file_path)}")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"打开文件失败: {str(e)}")
# 阅读界面 - 增强极简模式功能
class ReadingWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent
        self.chapter_list_visible = True
        self.buttons_visible = True
        self.minimal_mode = False  # 极简模式标志
        self.drag_position = None
        self.resize_border = 8  # 调整大小的边界宽度
        self.resize_direction = None
        self.custom_bg_color = None
        self.original_window_flags = None
        self.original_geometry = None
        self.current_font = QFont("微软雅黑", 12)  # 默认字体
        self.initUI()
    def initUI(self):
        main_layout = QHBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        # 使用QSplitter来管理左右布局
        self.splitter = QSplitter(Qt.Horizontal)
        # 左侧章节列表(可隐藏)
        self.left_frame = QFrame()
        self.left_frame.setFrameStyle(QFrame.StyledPanel)
        left_layout = QVBoxLayout(self.left_frame)
        left_layout.setContentsMargins(10, 10, 10, 10)
        chapter_label = QLabel("章节列表")
        chapter_label.setFont(QFont("微软雅黑", 12, QFont.Bold))
        chapter_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
        left_layout.addWidget(chapter_label)
        self.chapter_list = QListWidget()
        self.chapter_list.setFont(QFont("微软雅黑", 10))
        self.chapter_list.setStyleSheet("""
            QListWidget {
                border: 1px solid #ced4da;
                border-radius: 4px;
                background-color: white;
            }
            QListWidget::item {
                padding: 8px;
                border-bottom: 1px solid #e9ecef;
            }
            QListWidget::item:selected {
                background-color: #3498db;
                color: white;
            }
            QListWidget::item:hover {
                background-color: #e9ecef;
            }
        """)
        self.chapter_list.itemClicked.connect(self.on_chapter_clicked)
        left_layout.addWidget(self.chapter_list)
        # 隐藏/显示章节列表按钮
        self.toggle_chapter_btn = QPushButton("隐藏列表")
        self.toggle_chapter_btn.setFont(QFont("微软雅黑", 10))
        self.toggle_chapter_btn.setStyleSheet("""
            QPushButton {
                background-color: #6c757d;
                color: white;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #5a6268;
            }
        """)
        self.toggle_chapter_btn.clicked.connect(self.toggle_chapter_list)
        left_layout.addWidget(self.toggle_chapter_btn)
        self.splitter.addWidget(self.left_frame)
        # 右侧内容区域
        self.right_frame = QFrame()
        self.right_frame.setFrameStyle(QFrame.StyledPanel)
        right_layout = QVBoxLayout(self.right_frame)
        right_layout.setContentsMargins(15, 15, 15, 15)
        # 顶部导航栏
        nav_layout = QHBoxLayout()
        self.back_button = QPushButton("返回详情")
        self.back_button.setFont(QFont("微软雅黑", 10))
        self.back_button.setStyleSheet("""
            QPushButton {
                background-color: #95a5a6;
                color: white;
                padding: 8px 16px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #7f8c8d;
            }
        """)
        self.back_button.clicked.connect(self.go_back_to_detail)
        nav_layout.addWidget(self.back_button)
        self.novel_title_label = QLabel()
        self.novel_title_label.setFont(QFont("微软雅黑", 14, QFont.Bold))
        self.novel_title_label.setStyleSheet("color: #2c3e50;")
        nav_layout.addWidget(self.novel_title_label)
        nav_layout.addStretch()
        # 字体调节按钮
        self.font_button = QPushButton("字体调节")
        self.font_button.setFont(QFont("微软雅黑", 10))
        self.font_button.setStyleSheet("""
            QPushButton {
                background-color: #17a2b8;
                color: white;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #138496;
            }
        """)
        self.font_button.clicked.connect(self.adjust_font)
        nav_layout.addWidget(self.font_button)
        # 增强的显示/隐藏按钮(控制按钮组和章节列表)
        self.toggle_components_btn = QPushButton("隐藏组件")
        self.toggle_components_btn.setFont(QFont("微软雅黑", 10))
        self.toggle_components_btn.setStyleSheet("""
            QPushButton {
                background-color: #6c757d;
                color: white;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #5a6268;
            }
        """)
        self.toggle_components_btn.clicked.connect(self.toggle_components)
        nav_layout.addWidget(self.toggle_components_btn)
        # 背景色拾取按钮
        self.bg_color_btn = QPushButton("背景色")
        self.bg_color_btn.setFont(QFont("微软雅黑", 10))
        self.bg_color_btn.setStyleSheet("""
            QPushButton {
                background-color: #9b59b6;
                color: white;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #8e44ad;
            }
        """)
        self.bg_color_btn.clicked.connect(self.pick_background_color)
        nav_layout.addWidget(self.bg_color_btn)
        # 极简模式按钮
        self.minimal_mode_btn = QPushButton("极简模式")
        self.minimal_mode_btn.setFont(QFont("微软雅黑", 10))
        self.minimal_mode_btn.setStyleSheet("""
            QPushButton {
                background-color: #e74c3c;
                color: white;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #c0392b;
            }
        """)
        self.minimal_mode_btn.clicked.connect(self.toggle_minimal_mode)
        nav_layout.addWidget(self.minimal_mode_btn)
        right_layout.addLayout(nav_layout)
        # 按钮组
        self.button_frame = QFrame()
        button_layout = QHBoxLayout(self.button_frame)
        button_layout.setContentsMargins(0, 10, 0, 10)
        self.prev_btn = QPushButton("上一章")
        self.prev_btn.setFont(QFont("微软雅黑", 10))
        self.prev_btn.setStyleSheet("""
            QPushButton {
                background-color: #17a2b8;
                color: white;
                padding: 8px 16px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #138496;
            }
            QPushButton:disabled {
                background-color: #6c757d;
            }
        """)
        self.prev_btn.clicked.connect(self.prev_chapter)
        button_layout.addWidget(self.prev_btn)
        self.next_btn = QPushButton("下一章")
        self.next_btn.setFont(QFont("微软雅黑", 10))
        self.next_btn.setStyleSheet("""
            QPushButton {
                background-color: #28a745;
                color: white;
                padding: 8px 16px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #218838;
            }
            QPushButton:disabled {
                background-color: #6c757d;
            }
        """)
        self.next_btn.clicked.connect(self.next_chapter)
        button_layout.addWidget(self.next_btn)
        # 多功能按钮
        self.multi_function_btn = MultiFunctionButton("文件管理")
        self.multi_function_btn.setFont(QFont("微软雅黑", 10))
        self.multi_function_btn.setStyleSheet("""
            QPushButton {
                background-color: #ffc107;
                color: #212529;
                padding: 8px 16px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #e0a800;
            }
        """)
        button_layout.addWidget(self.multi_function_btn)
        right_layout.addWidget(self.button_frame)
        # 内容显示区域
        self.content_text = QTextEdit()
        self.content_text.setFont(self.current_font)
        self.content_text.setStyleSheet("""
            QTextEdit {
                border: 1px solid #ced4da;
                border-radius: 4px;
                padding: 15px;
                background-color: white;
                line-height: 1.6;
            }
        """)
        self.content_text.setReadOnly(True)
        right_layout.addWidget(self.content_text)
        self.splitter.addWidget(self.right_frame)
        # 设置分割比例
        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 3)
        # 保存初始大小
        self.splitter.setSizes([200, 600])
        main_layout.addWidget(self.splitter)
        self.setLayout(main_layout)
        # 设置鼠标跟踪
        self.setMouseTracking(True)
        self.content_text.setMouseTracking(True)
    def adjust_font(self):
        """字体调节功能"""
        font, ok = QFontDialog.getFont(self.current_font, self)
        if ok:
            self.current_font = font
            self.content_text.setFont(font)
            # 极简模式也会使用这个字体设置
    def set_content(self, novel_info, chapter_info, chapters):
        self.novel_info = novel_info
        self.chapter_info = chapter_info
        self.chapters = chapters
        self.current_index = chapter_info[0] - 1  # 转换为0-based索引
        # 更新界面
        self.novel_title_label.setText(f"{novel_info[1]} - {chapter_info[1]}")
        # 加载章节列表
        self.chapter_list.clear()
        for chapter in chapters:
            self.chapter_list.addItem(f"{chapter[0]}. {chapter[1]}")
        # 高亮当前章节
        if 0  0:
            self.current_index -= 1
            self.chapter_info = self.chapters[self.current_index]
            self.set_content(self.novel_info, self.chapter_info, self.chapters)
    def next_chapter(self):
        if self.current_index  0)
        self.next_btn.setEnabled(self.current_index = width - border and y = height - border:
            return "bottom-left"
        elif x >= width - border and y >= height - border:
            return "bottom-right"
        elif x = width - border:
            return "right"
        elif y = height - border:
            return "bottom"
        else:
            return None
    def update_cursor_shape(self, direction):
        """根据调整方向更新鼠标光标形状[8](@ref)"""
        if direction == "top-left" or direction == "bottom-right":
            self.parent.setCursor(Qt.SizeFDiagCursor)
        elif direction == "top-right" or direction == "bottom-left":
            self.parent.setCursor(Qt.SizeBDiagCursor)
        elif direction == "left" or direction == "right":
            self.parent.setCursor(Qt.SizeHorCursor)
        elif direction == "top" or direction == "bottom":
            self.parent.setCursor(Qt.SizeVerCursor)
        else:
            self.parent.setCursor(Qt.ArrowCursor)
    def mousePressEvent(self, event):
        """鼠标按下事件 - 支持窗口拖动和调整大小[8](@ref)"""
        if self.minimal_mode:
            if event.button() == Qt.LeftButton:
                # 检查是否在调整区域
                self.resize_direction = self.get_resize_direction(event.pos())
                if self.resize_direction:
                    # 开始调整大小
                    self.resize_start_pos = event.globalPos()
                    self.resize_start_geometry = self.parent.geometry()
                    event.accept()
                    return
                else:
                    # 记录拖动起始位置
                    self.drag_position = event.globalPos() - self.parent.frameGeometry().topLeft()
                    event.accept()
            elif event.button() == Qt.RightButton:
                # 右键退出极简模式
                self.exit_minimal_mode()
                event.accept()
        else:
            super().mousePressEvent(event)
    def mouseMoveEvent(self, event):
        """鼠标移动事件 - 处理窗口拖动和调整大小[8](@ref)"""
        if self.minimal_mode:
            if event.buttons() == Qt.LeftButton and self.resize_direction:
                # 调整窗口大小
                delta = event.globalPos() - self.resize_start_pos
                new_geometry = self.resize_start_geometry
                if "left" in self.resize_direction:
                    new_geometry.setLeft(new_geometry.left() + delta.x())
                if "right" in self.resize_direction:
                    new_geometry.setRight(new_geometry.right() + delta.x())
                if "top" in self.resize_direction:
                    new_geometry.setTop(new_geometry.top() + delta.y())
                if "bottom" in self.resize_direction:
                    new_geometry.setBottom(new_geometry.bottom() + delta.y())
                # 确保窗口有最小尺寸
                if new_geometry.width() = 0 and hasattr(self.parent, 'search_results'):
            novel_info = self.parent.search_results[index]
            self.parent.show_novel_detail(novel_info)
# 小说详情界面
class NovelDetailWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent
        self.initUI()
    def initUI(self):
        layout = QVBoxLayout()
        layout.setContentsMargins(20, 20, 20, 20)
        layout.setSpacing(15)
        # 返回按钮
        back_button = QPushButton("返回搜索")
        back_button.setFont(QFont("微软雅黑", 10))
        back_button.setStyleSheet("""
            QPushButton {
                background-color: #95a5a6;
                color: white;
                padding: 8px 16px;
                border: none;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #7f8c8d;
            }
        """)
        back_button.clicked.connect(self.parent.show_search)
        layout.addWidget(back_button)
        # 小说信息
        self.novel_info_label = QLabel()
        self.novel_info_label.setFont(QFont("微软雅黑", 14, QFont.Bold))
        self.novel_info_label.setStyleSheet("color: #2c3e50; margin: 10px 0;")
        self.novel_info_label.setWordWrap(True)
        layout.addWidget(self.novel_info_label)
        # 章节列表标签
        chapter_label = QLabel("章节列表")
        chapter_label.setFont(QFont("微软雅黑", 16, QFont.Bold))
        chapter_label.setStyleSheet("color: #2c3e50; margin: 10px 0;")
        layout.addWidget(chapter_label)
        # 章节列表
        self.chapter_list = QListWidget()
        self.chapter_list.setFont(QFont("微软雅黑", 10))
        self.chapter_list.setStyleSheet("""
            QListWidget {
                border: 1px solid #bdc3c7;
                border-radius: 5px;
                background-color: white;
                padding: 5px;
            }
            QListWidget::item {
                padding: 8px;
                border-bottom: 1px solid #ecf0f1;
            }
            QListWidget::item:selected {
                background-color: #3498db;
                color: white;
                border-radius: 3px;
            }
            QListWidget::item:hover {
                background-color: #ecf0f1;
                border-radius: 3px;
            }
        """)
        self.chapter_list.itemDoubleClicked.connect(self.on_chapter_selected)
        layout.addWidget(self.chapter_list)
        self.setLayout(layout)
    def set_novel_info(self, novel_info):
        self.novel_info = novel_info
        self.novel_info_label.setText(f"书名:《{novel_info[1]}》\n作者:{novel_info[2]}")
        # 加载章节列表
        self.parent.progress_bar.setVisible(True)
        self.chapter_thread = ChapterThread(novel_info[3])
        self.chapter_thread.finished.connect(self.on_chapters_loaded)
        self.chapter_thread.error.connect(self.on_chapters_error)
        self.chapter_thread.start()
    def on_chapters_loaded(self, chapters):
        self.parent.progress_bar.setVisible(False)
        self.chapter_list.clear()
        if not chapters:
            QMessageBox.warning(self, "错误", "加载章节失败")
            return
        self.chapters = chapters
        for chapter in chapters:
            self.chapter_list.addItem(f"{chapter[0]}. {chapter[1]}")
    def on_chapters_error(self, error_msg):
        self.parent.progress_bar.setVisible(False)
        QMessageBox.critical(self, "错误", f"加载章节失败: {error_msg}")
    def on_chapter_selected(self, item):
        index = self.chapter_list.currentRow()
        if index >= 0 and hasattr(self, 'chapters'):
            chapter_info = self.chapters[index]
            self.parent.show_reading(self.novel_info, chapter_info, self.chapters)
# 主窗口
class NovelReader(QMainWindow):
    def __init__(self):
        super().__init__()
        self.search_results = []
        self.initUI()
        session_ask()  # 初始化会话
    def initUI(self):
        self.setWindowTitle("小说在线阅读器")
        self.setGeometry(100, 100, 1200, 800)
        # 设置应用样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f8f9fa;
            }
        """)
        # 创建中央部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        layout.setContentsMargins(0, 0, 0, 0)
        # 全局进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: none;
                background-color: #e9ecef;
                height: 3px;
            }
            QProgressBar::chunk {
                background-color: #3498db;
            }
        """)
        layout.addWidget(self.progress_bar)
        # 创建堆叠窗口
        self.stacked_widget = QStackedWidget()
        layout.addWidget(self.stacked_widget)
        # 创建三个界面
        self.search_widget = SearchWidget(self)
        self.detail_widget = NovelDetailWidget(self)
        self.reading_widget = ReadingWidget(self)
        # 添加到堆叠窗口
        self.stacked_widget.addWidget(self.search_widget)
        self.stacked_widget.addWidget(self.detail_widget)
        self.stacked_widget.addWidget(self.reading_widget)
        # 显示搜索界面
        self.stacked_widget.setCurrentIndex(0)
    def show_search(self):
        self.stacked_widget.setCurrentIndex(0)
    def show_novel_detail(self, novel_info=None):
        if novel_info is not None:
            self.detail_widget.set_novel_info(novel_info)
        self.stacked_widget.setCurrentIndex(1)
    def show_reading(self, novel_info, chapter_info, chapters):
        self.reading_widget.set_content(novel_info, chapter_info, chapters)
        self.stacked_widget.setCurrentIndex(2)
if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 设置应用字体
    font = QFont("微软雅黑", 10)
    app.setFont(font)
    reader = NovelReader()
    reader.show()
    sys.exit(app.exec_())












宋体, 微软

Raohz520   

根据楼主提供的百度网盘下载到lanzou上了,
链接: https://pan.baidu.com/s/1IfIeeoP8j1xT7Z02qck0PA?pwd=dmhd 提取码: dmhd 复制这段内容后打开百度网盘手机App,操作更方便哦
下载:https://wwvi.lanzoue.com/igsuP37znq3i 密码:huw4
phantomxjc
OP
  

这不是欺负我word排版嘛
Raohz520   

有无打包后的程序
phantomxjc
OP
  


Raohz520 发表于 2025-10-9 11:50
有无打包后的程序

忘记加了  吃完饭回来发
lenbin521   

楼主好人 等一个程序
phantomxjc
OP
  


Raohz520 发表于 2025-10-9 11:50
有无打包后的程序

通过网盘分享的文件:小说摸鱼阅读器.exe
链接: https://pan.baidu.com/s/1IfIeeoP8j1xT7Z02qck0PA?pwd=dmhd 提取码: dmhd 复制这段内容后打开百度网盘手机App,操作更方便哦
phantomxjc
OP
  

打包后程序如下:通过网盘分享的文件:小说摸鱼阅读器.exe
链接: https://pan.baidu.com/s/1IfIeeoP8j1xT7Z02qck0PA?pwd=dmhd 提取码: dmhd 复制这段内容后打开百度网盘手机App,操作更方便哦
smallmouse228   

有没有其他网盘的?
林泽西   

失败……
您需要登录后才可以回帖 登录 | 立即注册

返回顶部