吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3639|回复: 16
收起左侧

[Python 原创] 小说摸鱼阅读器

[复制链接]
phantomxjc 发表于 2025-9-30 12:43
最近爱上了看小说,但是直接在电脑端网页上看小说,每次跳着章节看都要返回目录,重新开始找那个章节,
主打一个就很
烦!烦!烦!烦!
so,我就想着手机上的小说阅读器看着挺不错的,有章节导航,于是,搬过来,哈哈
很多功能都是见名知意,不过个人也加了一个特色小功能,叫做文件管理,
右键文件管理,选择一个txt文件,他会将其路径保存在一个txt当中,左键点击,则会将其内容显示在小说阅读界面,总之懂得都懂,哈哈哈哈

详细代码如下(不喜欢刨析代码,请别让我刨析):

[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)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QPropertyAnimation, QEasingCurve, QRect
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 = ''
        session.get(url=url, headers=headers, timeout=30)
    except Exception as e:
        print(f"Session初始化失败: {e}")


def novel(name):
    novels_list = []
    try:
        url = ''
        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 = '' + 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 = '' + 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 = '' + 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 = '' + 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 mousePressEvent(self, event: QMouseEvent):
        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 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 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.initUI()

    def initUI(self):
        main_layout = QHBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)

        # 使用QSplitter来管理左右布局[6,7,8](@ref)
        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()

        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.go_back_to_detail)
        nav_layout.addWidget(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.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)

        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(QFont("微软雅黑", 12))
        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)

        # 设置分割比例[7,8](@ref)
        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 3)

        # 保存初始大小
        self.splitter.setSizes([200, 600])

        main_layout.addWidget(self.splitter)
        self.setLayout(main_layout)

    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 <= self.current_index < self.chapter_list.count():
            self.chapter_list.setCurrentRow(self.current_index)

        # 更新按钮状态
        self.prev_btn.setEnabled(self.current_index > 0)
        self.next_btn.setEnabled(self.current_index < len(chapters) - 1)

        # 加载内容
        self.parent.progress_bar.setVisible(True)
        self.content_thread = ContentThread(chapter_info[2])
        self.content_thread.finished.connect(self.on_content_loaded)
        self.content_thread.error.connect(self.on_content_error)
        self.content_thread.start()

    def on_content_loaded(self, content):
        self.parent.progress_bar.setVisible(False)
        if content:
            # 格式化内容
            formatted_content = content.replace('\n', '\n\n')
            self.content_text.setPlainText(formatted_content)

            # 滚动到顶部
            self.content_text.verticalScrollBar().setValue(0)
        else:
            self.content_text.setPlainText("加载内容失败,请重试")

    def on_content_error(self, error_msg):
        self.parent.progress_bar.setVisible(False)
        self.content_text.setPlainText(f"加载内容失败: {error_msg}")

    def on_chapter_clicked(self, item):
        index = self.chapter_list.currentRow()
        if index != self.current_index and 0 <= index < len(self.chapters):
            self.current_index = index
            self.chapter_info = self.chapters[index]
            self.set_content(self.novel_info, self.chapter_info, self.chapters)

    def prev_chapter(self):
        if self.current_index > 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 < len(self.chapters) - 1:
            self.current_index += 1
            self.chapter_info = self.chapters[self.current_index]
            self.set_content(self.novel_info, self.chapter_info, self.chapters)

    def toggle_chapter_list(self):
        """切换章节列表的显示/隐藏 - 修复版"""
        if self.chapter_list_visible:
            # 隐藏章节列表
            self.splitter.setSizes([0, 800])
            self.toggle_chapter_btn.setText("显示列表")
            self.chapter_list_visible = False
        else:
            # 显示章节列表
            self.splitter.setSizes([200, 600])
            self.toggle_chapter_btn.setText("隐藏列表")
            self.chapter_list_visible = True

    def toggle_components(self):
        """增强的显示/隐藏功能:同时控制按钮组和章节列表"""
        if self.buttons_visible and self.chapter_list_visible:
            # 隐藏所有组件
            self.button_frame.hide()
            self.splitter.setSizes([0, 800])
            self.toggle_components_btn.setText("显示组件")
            self.buttons_visible = False
            self.chapter_list_visible = False
            # 同步更新章节列表按钮状态
            self.toggle_chapter_btn.setText("显示列表")
        else:
            # 显示所有组件
            self.button_frame.show()
            self.splitter.setSizes([200, 600])
            self.toggle_components_btn.setText("隐藏组件")
            self.buttons_visible = True
            self.chapter_list_visible = True
            # 同步更新章节列表按钮状态
            self.toggle_chapter_btn.setText("隐藏列表")

    def go_back_to_detail(self):
        # 返回详情界面
        self.parent.show_novel_detail()


# 搜索界面(保持不变)
class SearchWidget(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)

        # 标题
        title_label = QLabel("小说搜索阅读器")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setFont(QFont("微软雅黑", 20, QFont.Bold))
        title_label.setStyleSheet("color: #2c3e50; margin-bottom: 20px;")
        layout.addWidget(title_label)

        # 搜索框
        search_layout = QHBoxLayout()
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("请输入小说名称...")
        self.search_input.setFont(QFont("微软雅黑", 12))
        self.search_input.setStyleSheet("""
            QLineEdit {
                padding: 10px;
                border: 2px solid #bdc3c7;
                border-radius: 5px;
                font-size: 14px;
            }
            QLineEdit:focus {
                border-color: #3498db;
            }
        """)
        self.search_input.returnPressed.connect(self.search_novels)

        self.search_button = QPushButton("搜索")
        self.search_button.setFont(QFont("微软雅黑", 12))
        self.search_button.setStyleSheet("""
            QPushButton {
                background-color: #3498db;
                color: white;
                padding: 10px 20px;
                border: none;
                border-radius: 5px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #2980b9;
            }
            QPushButton:pressed {
                background-color: #21618c;
            }
        """)
        self.search_button.clicked.connect(self.search_novels)

        search_layout.addWidget(self.search_input)
        search_layout.addWidget(self.search_button)
        layout.addLayout(search_layout)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: 1px solid #bdc3c7;
                border-radius: 5px;
                text-align: center;
                background-color: #ecf0f1;
            }
            QProgressBar::chunk {
                background-color: #3498db;
                border-radius: 3px;
            }
        """)
        layout.addWidget(self.progress_bar)

        # 结果标签
        self.result_label = QLabel("搜索结果将显示在这里")
        self.result_label.setFont(QFont("微软雅黑", 10))
        self.result_label.setStyleSheet("color: #7f8c8d;")
        layout.addWidget(self.result_label)

        # 搜索结果列表
        self.result_list = QListWidget()
        self.result_list.setFont(QFont("微软雅黑", 11))
        self.result_list.setStyleSheet("""
            QListWidget {
                border: 1px solid #bdc3c7;
                border-radius: 5px;
                background-color: white;
                padding: 5px;
            }
            QListWidget::item {
                padding: 10px;
                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.result_list.itemDoubleClicked.connect(self.on_novel_selected)
        layout.addWidget(self.result_list)

        self.setLayout(layout)

    def search_novels(self):
        novel_name = self.search_input.text().strip()
        if not novel_name:
            QMessageBox.warning(self, "提示", "请输入小说名称")
            return

        self.progress_bar.setVisible(True)
        self.result_list.clear()
        self.result_label.setText("搜索中...")

        # 使用多线程进行搜索
        self.search_thread = SearchThread(novel_name)
        self.search_thread.finished.connect(self.on_search_finished)
        self.search_thread.error.connect(self.on_search_error)
        self.search_thread.start()

    def on_search_finished(self, result):
        self.progress_bar.setVisible(False)
        self.result_list.clear()

        if not result:
            self.result_label.setText("未找到相关小说")
            QMessageBox.information(self, "提示", "未找到相关小说")
            return

        self.result_label.setText(f"找到 {len(result)} 部相关小说")

        for novel_info in result:
            self.result_list.addItem(f"{novel_info[0]}. 《{novel_info[1]}》 - {novel_info[2]}")

        # 保存搜索结果供后续使用
        self.parent.search_results = result

    def on_search_error(self, error_msg):
        self.progress_bar.setVisible(False)
        self.result_label.setText("搜索失败")
        QMessageBox.critical(self, "错误", f"搜索失败: {error_msg}")

    def on_novel_selected(self, item):
        index = self.result_list.currentRow()
        if index >= 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(chapter_info)


# 主窗口(保持不变)
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;
            }
            QWidget {
                background-color: white;
            }
        """)

        # 创建中央部件和布局
        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)

        # 创建堆叠窗口[3,4](@ref)
        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, chapter_info):
        if hasattr(self.detail_widget, 'novel_info') and hasattr(self.detail_widget, 'chapters'):
            self.reading_widget.set_content(
                self.detail_widget.novel_info,
                chapter_info,
                self.detail_widget.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_())


效果图如下: 1.png 2.png 3.png 4.png

百度网盘链接如下:
通过网盘分享的文件:小说阅读器.exe
链接: https://pan.baidu.com/s/1_G9hPWLvU5rzujYOCde6Tg?pwd=jgx3 提取码: jgx3 复制这段内容后打开百度网盘手机App,操作更方便哦
夸克网盘:
我用夸克网盘给你分享了「小说阅读器.exe」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/~2c8738UpNi~:/
链接:https://pan.quark.cn/s/c8bb213c869e

免费评分

参与人数 3吾爱币 +7 热心值 +3 收起 理由
zweni + 1 + 1 用心讨论,共获提升!
hrh123 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
alidadai + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yxf515321 发表于 2025-10-1 18:36
万能的QT呀,应该再添加上调整页面字体、字符大小和背景颜色的功能。或者考虑边框虚化,背景和桌面同色,这样就能顺利摸鱼了
属于你 发表于 2025-9-30 13:39
kaimenzzz 发表于 2025-9-30 13:41
逝去的初夏 发表于 2025-9-30 13:44
看出了便捷下载,没看出摸鱼,生怕发现不了我
qianjieqaq 发表于 2025-9-30 14:37
不够隐蔽呀,之前有一个单行阅读器非常隐蔽
Vampiremss 发表于 2025-9-30 15:04
多谢分享,试用一下
vip-happy 发表于 2025-9-30 15:07
不错的阅读器,支持原创。
期待下一版可以调节字体大小和颜色。
naixubao 发表于 2025-9-30 15:12
是个好工具,期待后续的摸鱼,感谢楼主分享。
Yanpeen 发表于 2025-9-30 15:12
没有老板键,不敢摸鱼了
malio9950 发表于 2025-9-30 21:57
不能调节行距,字体大小。摸鱼至少需要搞个透明背景,文字不透明度可以调节吧?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-8-28 04:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表