首页 > 编程语言 >使用Python库pyqt5制作TXT阅读器(三)-------显示内容和选择章节

使用Python库pyqt5制作TXT阅读器(三)-------显示内容和选择章节

时间:2023-01-09 15:23:21浏览次数:62  
标签:last QtGui Python self pyqt5 ------- file translate MainWindow

项目地址:https://github.com/pikeduo/TXTReader
PyQt5中文手册:https://maicss.gitbook.io/pyqt-chinese-tutoral/pyqt5/
QtDesigner学习地址:https://youcans.blog.csdn.net/article/details/120640342

一、设置章节目录

首先我们来说明一下分割章节的原理。打开文件时,将每一行作为一个元素存入数组,逐行比较判断该行是否存在章节名称,并记录章节名所在的位置。
点击章节时,根据数组小标位置来判断其所在的行,去该行到下一行的内容显示到文本浏览器中。

                with open(file, 'r', encoding=encodings) as f:
                    # 打开文件,生成章节目录
                    self.chapters = []
                    # 包含了txt文本的全部内容
                    self.lines = f.readlines()
                    # 一种匹配章节目录的规则
                    pattern = r"(第)([\u4e00-\u9fa5a-zA-Z0-9]{1,7})[章|节][^\n]{1,35}(|\n)"
                    for i in range(len(self.lines)):
                        line = self.lines[i].strip()
                        if line != "" and re.match(pattern, line):
                            line = line.replace("\n", "").replace("=", "")
                            if len(line) < 30:
                                self.chapters.append({line: i})

将匹配到的章节名作为字典的key,其所在的列作为value,将字典存入数组中去。
有些文本不具有可用的章节名,那就将文件名作为key,首行(0)作为value,存入数组。
可以将此段代码单独拿出来看看生成的数据是什么样的。

# 如果没有可用的目录,那就显示全部
                if not self.chapters:
                    self.chapters.append({self.filename: 0})

加载新文件时将当前目录设置为第一章。

# 打开新文件时重置章节
                if not self.last_files or self.cur_file != self.last_files[-1]:
                    self.chapter = 0

随后,我们来绘制章节目录。

    # 设置章节目录
    def setChapters(self):
        # 每次绘制目录时先清除一下
        self.treeWidget.clear()
        _translate = QtCore.QCoreApplication.translate
        __sortingEnabled = self.treeWidget.isSortingEnabled()
        for i, value in enumerate(self.chapters):
            item = QTreeWidgetItem(self.treeWidget)
            item.setText(0, _translate("MyMainWindow", list(value.keys())[0]))
            self.treeWidget.addTopLevelItem(item)
        self.treeWidget.setSortingEnabled(__sortingEnabled)
        self.treeWidget.clicked.connect(self.onTreeClicked)
        self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(self.chapter),0)
        # 为当前章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))

循环添加章节目录,并且在页面显示当前的目录,突出显示目前的章节。self.chapter代表目前的章节,默认是0,在配置里加上这个属性,同时在加载和保存配置是添加上这个属性。
接下来设置触发事件。

    # 点击目录跳转到章节
    def onTreeClicked(self, index):
        # 恢复原来章节的背景色(设置透明度为0),为新章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0,0,0, 0))
        # 获取点击的项目下标
        self.chapter = int(index.row())
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))
        # 显示内容
        self.show_content()
    # 设置文本浏览器的内容
    def show_content(self):
        # 将文件内容添加到文本浏览器中
        self.textBrowser.setText(self.get_content())
        # 状态栏显示当前的章节内容和目录名
        self.textBrowser.setStatusTip(self.filename + "   " +list(self.chapters[self.chapter].keys())[0])
    # 获取章节内容
    def get_content(self):
        index = self.chapter
        # 起始行
        start = list(self.chapters[index].values())[0]
        # 如果是终章
        if index == self.treeWidget.topLevelItemCount() - 1:
            return "".join(self.lines[start:-1])
        else:
            # 终止行
            end = list(self.chapters[index + 1].values())[0]
            return "".join(self.lines[start:end])

第一章之前的内容将不会再显示,最后一章会将后面的内容全部显示出来。
在打开文件的函数里调用下面这两个函数。

                # 设置章节目录
                self.setChapters()
                # 设置文本浏览器的内容
                self.show_content()

现在就可以选择章节了。
在这里插入图片描述
完整代码。

import ctypes
import re
import sys

import cchardet as cchardet
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QMessageBox, QTreeWidgetItem

from QtDesigner.UI.UIReader import Ui_MainWindow

# 加上这段话,在运行程序时,设置的窗口图标才会出现
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")

class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)  # 初始化父类
        self.setupUi(self)  # 继承 Ui_MainWindow 界面类
        # 加载设置
        self.init_info()
        # 如果之前打开过文件,则直接加载文件
        if self.cur_file:
            self.load_file(self.cur_file)
        # 打开文件
        self.actionfile.triggered.connect(self.open_file)

    # 打开文件
    def open_file(self):
        # 弹出QFileDialog窗口。getOpenFileName()方法的第一个参数是说明文字,
        # 第二个参数是默认打开的文件夹路径。默认情况下显示所有类型的文件。
        if not self.cur_file:
            path = '/'
        else:
            path = self.cur_file
        fname = QFileDialog.getOpenFileName(self, '打开文件', path, filter='*.txt')
        self.load_file(fname[0])

    # 显示最近的文件
    def show_last_file(self):
        # 每次绘制时将之前的清空
        self.lastfile.clear()
        _translate = QtCore.QCoreApplication.translate
        for i, file in enumerate(self.last_files):
            # 截取文件名
            name = file.split('/')[-1].split('.')[0]
            # 添加action
            action = QtWidgets.QAction(self)
            action.setObjectName(f'file{i}')    # 设置对象名
            self.lastfile.addAction(action)  # 添加到菜单栏中
            action.setText(_translate("MyMainWindow", name))    # 添加到主窗口,且设置text
            action.triggered.connect(self.open_last_file)   # 设置触发事件

    # 打开最近的文件
    def open_last_file(self):
        sender = self.sender().objectName()  # 获取当前信号 sender
        # 根据我们设置的对象名,截取字符,然后从配置文件寻找文件路径
        self.load_file(self.last_files[int(sender[-1])])

    def load_file(self, file):
        # 文件不为空
        if file:
            try:
            	# 打开新文件时重置章节
                if not self.last_files or self.cur_file != self.last_files[-1]:
                    self.chapter = 0
                # 更改目前打开的文件
                self.cur_file = file
                self.filename = file.split('/')[-1].split('.')[0]
                # 将打开的文件添加到最近文件中去
                # 如果文件存在,则要更改文件的打开顺序
                if file in self.last_files:
                    self.last_files.remove(file)
                self.last_files.append(file)
                # 只存储最近打开的五本书籍
                if len(self.last_files) > 5:
                    self.last_files.pop(0)
                # 获取文件的编码格式
                encodings = self.get_encoding(file)
                with open(file, 'r', encoding=encodings) as f:
                    # 打开文件,生成章节目录
                    self.chapters = []
                    # 包含了txt文本的全部内容
                    self.lines = f.readlines()
                    # 一种匹配章节目录的规则
                    pattern = r"(第)([\u4e00-\u9fa5a-zA-Z0-9]{1,7})[章|节][^\n]{1,35}(|\n)"
                    for i in range(len(self.lines)):
                        line = self.lines[i].strip()
                        if line != "" and re.match(pattern, line):
                            line = line.replace("\n", "").replace("=", "")
                            if len(line) < 30:
                                self.chapters.append({line: i})
                 # 如果没有可用的目录,那就显示全部
                if not self.chapters:
                    self.chapters.append({self.filename: 0})
                # print(self.chapters)
                # 显示最近打开的文件
                self.show_last_file()
                # 设置章节目录
                self.setChapters()
                # 设置文本浏览器的内容
                self.show_content()
            except:
                self.show_msg('文件不存在或者错误!')
        else:   # 文件为空,说明没有选择文件
            self.show_msg('您没有选择文件!')

    # 设置章节目录
    def setChapters(self):
        # 每次绘制目录时先清除一下
        self.treeWidget.clear()
        _translate = QtCore.QCoreApplication.translate
        __sortingEnabled = self.treeWidget.isSortingEnabled()
        for i, value in enumerate(self.chapters):
            item = QTreeWidgetItem(self.treeWidget)
            item.setText(0, _translate("MyMainWindow", list(value.keys())[0]))
            self.treeWidget.addTopLevelItem(item)
        self.treeWidget.setSortingEnabled(__sortingEnabled)
        self.treeWidget.clicked.connect(self.onTreeClicked)
        self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(self.chapter),0)
        # 为当前章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))

    # 点击目录跳转到章节
    def onTreeClicked(self, index):
        # 恢复原来章节的背景色(设置透明度为0),为新章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0,0,0, 0))
        # 获取点击的项目下标
        self.chapter = int(index.row())
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))
        # 显示内容
        self.show_content()

    # 设置文本浏览器的内容
    def show_content(self):
        # 将文件内容添加到文本浏览器中
        self.textBrowser.setText(self.get_content())
        # 状态栏显示当前的章节内容和目录名
        self.textBrowser.setStatusTip(self.filename + "   " +list(self.chapters[self.chapter].keys())[0])

    # 获取章节内容
    def get_content(self):
        index = self.chapter
        # 起始行
        start = list(self.chapters[index].values())[0]
        # 如果是终章
        if index == self.treeWidget.topLevelItemCount() - 1:
            return "".join(self.lines[start:-1])
        else:
            # 终止行
            end = list(self.chapters[index + 1].values())[0]
            return "".join(self.lines[start:end])

    def show_msg(self, msg):
        # 后两项分别为按钮(以|隔开,共有7种按钮类型,见示例后)、默认按钮(省略则默认为第一个按钮)
        reply = QMessageBox.information(self, "提示", msg, QMessageBox.Yes | QMessageBox.No,
                                        QMessageBox.Yes)

    # 获取文件编码类型
    def get_encoding(self, file):
        # 二进制方式读取,获取字节数据,检测类型
        with open(file, 'rb') as f:
            return cchardet.detect(f.read())['encoding']

    # 初始化设置
    def init_info(self):
        self.setting = QtCore.QSettings("./config.ini", QtCore.QSettings.IniFormat)   # 配置文件
        self.setting.setIniCodec('utf-8')   # 设置配置文件的编码格式
        self.cur_file = self.setting.value("FILE/file")     # 目前打开的文件
        self.last_files = self.setting.value("FILE/files")  # 最近打开的文件
        if not self.last_files:
            self.last_files = []
        self.chapter = int(self.setting.value("FILE/chapter"))  # 上次浏览的章节
    # 保存设置
    def save_info(self):
        self.setting.setValue("FILE/file", self.cur_file)
        self.setting.setValue("FILE/files", self.last_files)
        self.setting.setValue("FILE/chapter", self.chapter)

    # 关闭窗口时更新窗口大小
    def closeEvent(self, event):
        result = QMessageBox.question(self, "关闭应用", "确定关闭应用?",
                                      QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if result == QMessageBox.Yes:
            # 在关闭窗口时保存设置
            self.save_info()
            event.accept()
            QMainWindow.closeEvent(self, event)
        else:
            event.ignore()

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序

二、添加按钮

首先我们要设置两个按,分别代表上一章和下一章。
从左边,选择一个水平布局移到文本浏览器下面,如何选择两个按钮,移到右上角对象的水平布局下,这样子两个按键就设置好了。
将按钮设置成靠左和靠右,添加图标,这时会发现居右的按钮图标在文本左边。
在这里插入图片描述
不用担心,在属性编辑器里找到layoutDirection,设置为从右到左。
在这里插入图片描述
但是要注意,右边的按钮要设置为居左,否则就会变成下面这个样子。
在这里插入图片描述
这样就完成了UI界面的设置,编译运行。
在这里插入图片描述
ui界面的代码如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'UIReader.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1280, 720)
        MainWindow.setMinimumSize(QtCore.QSize(1280, 720))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/icon/reader.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.last = QtWidgets.QPushButton(self.centralwidget)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(":/icon/last.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.last.setIcon(icon1)
        self.last.setObjectName("last")
        self.horizontalLayout.addWidget(self.last, 0, QtCore.Qt.AlignLeft)
        self.next = QtWidgets.QPushButton(self.centralwidget)
        self.next.setLayoutDirection(QtCore.Qt.RightToLeft)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap(":/icon/next.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.next.setIcon(icon2)
        self.next.setObjectName("next")
        self.horizontalLayout.addWidget(self.next, 0, QtCore.Qt.AlignLeft)
        self.gridLayout.addLayout(self.horizontalLayout, 1, 1, 1, 1)
        self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
        self.treeWidget.setObjectName("treeWidget")
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap(":/icon/catlogs.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.treeWidget.headerItem().setIcon(0, icon3)
        self.gridLayout.addWidget(self.treeWidget, 0, 0, 1, 1, QtCore.Qt.AlignLeft)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        self.textBrowser.setObjectName("textBrowser")
        self.gridLayout.addWidget(self.textBrowser, 0, 1, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 26))
        self.menubar.setObjectName("menubar")
        self.files = QtWidgets.QMenu(self.menubar)
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap(":/icon/files.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.files.setIcon(icon4)
        self.files.setObjectName("files")
        self.lastfile = QtWidgets.QMenu(self.files)
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap(":/icon/file_last.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.lastfile.setIcon(icon5)
        self.lastfile.setObjectName("lastfile")
        self.setting = QtWidgets.QMenu(self.menubar)
        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap(":/icon/setting.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setting.setIcon(icon6)
        self.setting.setObjectName("setting")
        self.fontcolor = QtWidgets.QMenu(self.setting)
        icon7 = QtGui.QIcon()
        icon7.addPixmap(QtGui.QPixmap(":/icon/fontcolor.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.fontcolor.setIcon(icon7)
        self.fontcolor.setObjectName("fontcolor")
        self.bg = QtWidgets.QMenu(self.setting)
        icon8 = QtGui.QIcon()
        icon8.addPixmap(QtGui.QPixmap(":/icon/bg.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.bg.setIcon(icon8)
        self.bg.setObjectName("bg")
        self.exit = QtWidgets.QMenu(self.menubar)
        icon9 = QtGui.QIcon()
        icon9.addPixmap(QtGui.QPixmap(":/icon/exit.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.exit.setIcon(icon9)
        self.exit.setObjectName("exit")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionfile = QtWidgets.QAction(MainWindow)
        icon10 = QtGui.QIcon()
        icon10.addPixmap(QtGui.QPixmap(":/icon/file.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionfile.setIcon(icon10)
        self.actionfile.setObjectName("actionfile")
        self.action1 = QtWidgets.QAction(MainWindow)
        self.action1.setObjectName("action1")
        self.actionfont = QtWidgets.QAction(MainWindow)
        icon11 = QtGui.QIcon()
        icon11.addPixmap(QtGui.QPixmap(":/icon/font.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionfont.setIcon(icon11)
        self.actionfont.setObjectName("actionfont")
        self.actioncolor = QtWidgets.QAction(MainWindow)
        icon12 = QtGui.QIcon()
        icon12.addPixmap(QtGui.QPixmap(":/icon/color.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actioncolor.setIcon(icon12)
        self.actioncolor.setObjectName("actioncolor")
        self.actionimport = QtWidgets.QAction(MainWindow)
        icon13 = QtGui.QIcon()
        icon13.addPixmap(QtGui.QPixmap(":/icon/import.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionimport.setIcon(icon13)
        self.actionimport.setObjectName("actionimport")
        self.actionclose = QtWidgets.QAction(MainWindow)
        icon14 = QtGui.QIcon()
        icon14.addPixmap(QtGui.QPixmap(":/icon/close.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionclose.setIcon(icon14)
        self.actionclose.setObjectName("actionclose")
        self.actiondefault = QtWidgets.QAction(MainWindow)
        icon15 = QtGui.QIcon()
        icon15.addPixmap(QtGui.QPixmap(":/icon/default.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actiondefault.setIcon(icon15)
        self.actiondefault.setObjectName("actiondefault")
        self.actionexit = QtWidgets.QAction(MainWindow)
        self.actionexit.setIcon(icon9)
        self.actionexit.setObjectName("actionexit")
        self.files.addAction(self.actionfile)
        self.files.addAction(self.lastfile.menuAction())
        self.fontcolor.addAction(self.actionfont)
        self.fontcolor.addAction(self.actioncolor)
        self.bg.addAction(self.actionimport)
        self.bg.addAction(self.actionclose)
        self.setting.addAction(self.fontcolor.menuAction())
        self.setting.addAction(self.bg.menuAction())
        self.setting.addAction(self.actiondefault)
        self.exit.addAction(self.actionexit)
        self.menubar.addAction(self.files.menuAction())
        self.menubar.addAction(self.setting.menuAction())
        self.menubar.addAction(self.exit.menuAction())

        self.retranslateUi(MainWindow)
        self.actionexit.triggered.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "TXT阅读器"))
        MainWindow.setStatusTip(_translate("MainWindow", "TXT阅读器"))
        self.last.setToolTip(_translate("MainWindow", "上一章"))
        self.last.setStatusTip(_translate("MainWindow", "上一章"))
        self.last.setText(_translate("MainWindow", "上一章"))
        self.next.setToolTip(_translate("MainWindow", "下一章"))
        self.next.setStatusTip(_translate("MainWindow", "下一章"))
        self.next.setText(_translate("MainWindow", "下一章"))
        self.treeWidget.setToolTip(_translate("MainWindow", "章节目录"))
        self.treeWidget.setStatusTip(_translate("MainWindow", "章节目录"))
        self.treeWidget.headerItem().setText(0, _translate("MainWindow", "章节目录"))
        self.files.setStatusTip(_translate("MainWindow", "文件"))
        self.files.setTitle(_translate("MainWindow", "文件"))
        self.lastfile.setToolTip(_translate("MainWindow", "打开最近的文件"))
        self.lastfile.setStatusTip(_translate("MainWindow", "打开最近的文件"))
        self.lastfile.setTitle(_translate("MainWindow", "打开最近的文件"))
        self.setting.setToolTip(_translate("MainWindow", "设置"))
        self.setting.setStatusTip(_translate("MainWindow", "设置"))
        self.setting.setTitle(_translate("MainWindow", "设置"))
        self.fontcolor.setTitle(_translate("MainWindow", "字体和颜色"))
        self.bg.setTitle(_translate("MainWindow", "背景图片"))
        self.exit.setToolTip(_translate("MainWindow", "关闭应用"))
        self.exit.setStatusTip(_translate("MainWindow", "关闭应用"))
        self.exit.setTitle(_translate("MainWindow", "退出"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionfile.setText(_translate("MainWindow", "打开文件"))
        self.actionfile.setStatusTip(_translate("MainWindow", "打开文件"))
        self.actionfile.setShortcut(_translate("MainWindow", "Ctrl+O"))
        self.action1.setText(_translate("MainWindow", "1"))
        self.actionfont.setText(_translate("MainWindow", "选择字体"))
        self.actionfont.setStatusTip(_translate("MainWindow", "选择字体"))
        self.actioncolor.setText(_translate("MainWindow", "选择背景颜色"))
        self.actioncolor.setStatusTip(_translate("MainWindow", "选择背景颜色"))
        self.actionimport.setText(_translate("MainWindow", "导入背景图片"))
        self.actionimport.setStatusTip(_translate("MainWindow", "导入背景图片"))
        self.actionclose.setText(_translate("MainWindow", "关闭背景图片"))
        self.actionclose.setStatusTip(_translate("MainWindow", "关闭背景图片"))
        self.actiondefault.setText(_translate("MainWindow", "恢复默认设置"))
        self.actiondefault.setStatusTip(_translate("MainWindow", "恢复默认设置"))
        self.actionexit.setText(_translate("MainWindow", "退出应用"))
        self.actionexit.setStatusTip(_translate("MainWindow", "退出应用"))
        self.actionexit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
import resource_rc

三、切换章节

首先为两个按钮设置触发事件。

# 上一章,下一章按钮点击事件
        self.last.clicked.connect(self.show_last)
        self.next.clicked.connect(self.show_next)
    # 展示上一章
    def show_last(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter - 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))
    # 展示下一章
    def show_next(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter + 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))

现在可以点击按钮切换上一章或者下一章了。
在这里插入图片描述
但是还有一个问题,当显示第一章和最后一章时,点击按钮会出现问题,因此我们还要再做一些改动。

    # 根据显示的章节来决定按钮是否要改动,简单一点,直接隐藏按钮
    def show_button(self):
        # 考虑只有一个章节的情况
        if len(self.chapters) == 1:
            self.last.setHidden(True)
            self.next.setHidden(True)
        # 第一章
        elif self.chapter == 0:
            self.last.setHidden(True)
            self.next.setVisible(True)
        # 末章
        elif self.chapter == len(self.chapters) - 1:
            self.last.setVisible(True)
            self.next.setHidden(True)
        # 其他情况,恢复按钮
        else:
            if self.last.isHidden():
                self.last.setVisible(True)
            if self.next.isHidden():
                self.next.setVisible(True)

上面的函数是判断按钮是否要显示,另外还要在初始化界面因此按钮

        # 开局先隐藏按钮
        self.last.setHidden(True)
        self.next.setHidden(True)

在显示内容时判断按是否要显示。

    # 设置文本浏览器的内容
    def show_content(self):
        # 在展示内容时直接判断按钮是否要隐藏
        self.show_button()
        # 将文件内容添加到文本浏览器中
        self.textBrowser.setText(self.get_content())
        # 状态栏显示当前的章节内容和目录名
        self.textBrowser.setStatusTip(self.filename + "   " +list(self.chapters[self.chapter].keys())[0])

在选择章节目录时也要进行判断。

    # 点击目录跳转到章节
    def onTreeClicked(self, index):
        # 恢复原来章节的背景色(设置透明度为0),为新章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0,0,0, 0))
        # 获取点击的项目下标
        self.chapter = int(index.row())
        # 判断按钮是否要显示
        self.show_button()
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))
        self.show_content()

切换章节的内容设计完成了。
在这里插入图片描述
完整代码:

import ctypes
import re
import sys

import cchardet as cchardet
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QMessageBox, QTreeWidgetItem

from QtDesigner.UI.UIReader import Ui_MainWindow

# 加上这段话,在运行程序时,设置的窗口图标才会出现
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")

class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)  # 初始化父类
        self.setupUi(self)  # 继承 Ui_MainWindow 界面类
        # 加载设置
        self.init_info()
        # 开局先隐藏按钮
        self.last.setHidden(True)
        self.next.setHidden(True)
        # 如果之前打开过文件,则直接加载文件
        if self.cur_file:
            self.load_file(self.cur_file)
        # 打开文件
        self.actionfile.triggered.connect(self.open_file)
        # 上一章,下一章按钮点击事件
        self.last.clicked.connect(self.show_last)
        self.next.clicked.connect(self.show_next)

    # 打开文件
    def open_file(self):
        # 弹出QFileDialog窗口。getOpenFileName()方法的第一个参数是说明文字,
        # 第二个参数是默认打开的文件夹路径。默认情况下显示所有类型的文件。
        if not self.cur_file:
            path = '/'
        else:
            path = self.cur_file
        fname = QFileDialog.getOpenFileName(self, '打开文件', path, filter='*.txt')
        self.load_file(fname[0])

    # 显示最近的文件
    def show_last_file(self):
        # 每次绘制时将之前的清空
        self.lastfile.clear()
        _translate = QtCore.QCoreApplication.translate
        for i, file in enumerate(self.last_files):
            # 截取文件名
            name = file.split('/')[-1].split('.')[0]
            # 添加action
            action = QtWidgets.QAction(self)
            action.setObjectName(f'file{i}')    # 设置对象名
            self.lastfile.addAction(action)  # 添加到菜单栏中
            action.setText(_translate("MyMainWindow", name))    # 添加到主窗口,且设置text
            action.triggered.connect(self.open_last_file)   # 设置触发事件

    # 打开最近的文件
    def open_last_file(self):
        sender = self.sender().objectName()  # 获取当前信号 sender
        # 根据我们设置的对象名,截取字符,然后从配置文件寻找文件路径
        self.load_file(self.last_files[int(sender[-1])])

    def load_file(self, file):
        # 文件不为空
        if file:
            try:
            	# 打开新文件时重置章节
                if not self.last_files or self.cur_file != self.last_files[-1]:
                    self.chapter = 0
                # 更改目前打开的文件
                self.cur_file = file
                self.filename = file.split('/')[-1].split('.')[0]
                # 将打开的文件添加到最近文件中去
                # 如果文件存在,则要更改文件的打开顺序
                if file in self.last_files:
                    self.last_files.remove(file)
                self.last_files.append(file)
                # 只存储最近打开的五本书籍
                if len(self.last_files) > 5:
                    self.last_files.pop(0)
                # 获取文件的编码格式
                encodings = self.get_encoding(file)
                with open(file, 'r', encoding=encodings) as f:
                    # 打开文件,生成章节目录
                    self.chapters = []
                    # 包含了txt文本的全部内容
                    self.lines = f.readlines()
                    # 一种匹配章节目录的规则
                    pattern = r"(第)([\u4e00-\u9fa5a-zA-Z0-9]{1,7})[章|节][^\n]{1,35}(|\n)"
                    for i in range(len(self.lines)):
                        line = self.lines[i].strip()
                        if line != "" and re.match(pattern, line):
                            line = line.replace("\n", "").replace("=", "")
                            if len(line) < 30:
                                self.chapters.append({line: i})
                 # 如果没有可用的目录,那就显示全部
                if not self.chapters:
                    self.chapters.append({self.filename: 0})
                # print(self.chapters)
                # 显示最近打开的文件
                self.show_last_file()
                # 设置章节目录
                self.setChapters()
                # 设置文本浏览器的内容
                self.show_content()
            except:
                self.show_msg('文件不存在或者错误!')
        else:   # 文件为空,说明没有选择文件
            self.show_msg('您没有选择文件!')

    # 设置章节目录
    def setChapters(self):
        # 每次绘制目录时先清除一下
        self.treeWidget.clear()
        _translate = QtCore.QCoreApplication.translate
        __sortingEnabled = self.treeWidget.isSortingEnabled()
        for i, value in enumerate(self.chapters):
            item = QTreeWidgetItem(self.treeWidget)
            item.setText(0, _translate("MyMainWindow", list(value.keys())[0]))
            self.treeWidget.addTopLevelItem(item)
        self.treeWidget.setSortingEnabled(__sortingEnabled)
        self.treeWidget.clicked.connect(self.onTreeClicked)
        self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(self.chapter),0)
        # 为当前章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))

    # 点击目录跳转到章节
    def onTreeClicked(self, index):
        # 恢复原来章节的背景色(设置透明度为0),为新章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0,0,0, 0))
        # 获取点击的项目下标
        self.chapter = int(index.row())
        # 判断按钮是否要显示
        self.show_button()
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))
        self.show_content()


    # 展示上一章
    def show_last(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter - 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))

    # 展示下一章
    def show_next(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter + 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))

    # 设置文本浏览器的内容
    def show_content(self):
        # 在展示内容时直接判断按钮是否要隐藏
        self.show_button()
        # 将文件内容添加到文本浏览器中
        self.textBrowser.setText(self.get_content())
        # 状态栏显示当前的章节内容和目录名
        self.textBrowser.setStatusTip(self.filename + "   " +list(self.chapters[self.chapter].keys())[0])

    # 获取章节内容
    def get_content(self):
        index = self.chapter
        # 起始行
        start = list(self.chapters[index].values())[0]
        # 如果是终章
        if index == self.treeWidget.topLevelItemCount() - 1:
            return "".join(self.lines[start:-1])
        else:
            # 终止行
            end = list(self.chapters[index + 1].values())[0]
            return "".join(self.lines[start:end])

    # 根据显示的章节来决定按钮是否要改动,简单一点,直接隐藏按钮
    def show_button(self):
        # 考虑只有一个章节的情况
        if len(self.chapters) == 1:
            self.last.setHidden(True)
            self.next.setHidden(True)
        # 第一章
        elif self.chapter == 0:
            self.last.setHidden(True)
            self.next.setVisible(True)
         # 末章
        elif self.chapter == len(self.chapters) - 1:
            self.last.setVisible(True)
            self.next.setHidden(True)
        # 其他情况,恢复按钮
        else:
            if self.last.isHidden():
                self.last.setVisible(True)
            if self.next.isHidden():
                self.next.setVisible(True)

    def show_msg(self, msg):
        # 后两项分别为按钮(以|隔开,共有7种按钮类型,见示例后)、默认按钮(省略则默认为第一个按钮)
        reply = QMessageBox.information(self, "提示", msg, QMessageBox.Yes | QMessageBox.No,
                                        QMessageBox.Yes)

    # 获取文件编码类型
    def get_encoding(self, file):
        # 二进制方式读取,获取字节数据,检测类型
        with open(file, 'rb') as f:
            return cchardet.detect(f.read())['encoding']

    # 初始化设置
    def init_info(self):
        self.setting = QtCore.QSettings("./config.ini", QtCore.QSettings.IniFormat)   # 配置文件
        self.setting.setIniCodec('utf-8')   # 设置配置文件的编码格式
        self.cur_file = self.setting.value("FILE/file")     # 目前打开的文件
        self.last_files = self.setting.value("FILE/files")  # 最近打开的文件
        if not self.last_files:
            self.last_files = []
        self.chapter = int(self.setting.value("FILE/chapter"))  # 上次浏览的章节
    # 保存设置
    def save_info(self):
        self.setting.setValue("FILE/file", self.cur_file)
        self.setting.setValue("FILE/files", self.last_files)
        self.setting.setValue("FILE/chapter", self.chapter)

    # 关闭窗口时更新窗口大小
    def closeEvent(self, event):
        result = QMessageBox.question(self, "关闭应用", "确定关闭应用?",
                                      QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if result == QMessageBox.Yes:
            # 在关闭窗口时保存设置
            self.save_info()
            event.accept()
            QMainWindow.closeEvent(self, event)
        else:
            event.ignore()

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序

四、显示/隐藏目录

首先我们在工具栏里设置一个目录图标。
在右下角选择动作编辑器,添加一个目录的动作。
在这里插入图片描述
将其拖到主页面的工具栏位置,这样就添加完成了。
在这里插入图片描述
保存编译,为目录添加触发事件。

# 点击目录显示/隐藏目录
        self.catlog.triggered.connect(self.tab_catlog)
    # 显示/隐藏目录
    def tab_catlog(self):
        if self.treeWidget.isVisible():
            self.treeWidget.setHidden(True)
        else:
            self.treeWidget.setVisible(True)

在初始化时设置目录为隐藏状态。

        # 设置目录栏初始为隐藏状态
        self.treeWidget.setHidden(True)

现在目录可以显示和隐藏了。
在这里插入图片描述
UI界面完整代码。

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'UIReader.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1280, 720)
        MainWindow.setMinimumSize(QtCore.QSize(1280, 720))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/icon/reader.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.last = QtWidgets.QPushButton(self.centralwidget)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(":/icon/last.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.last.setIcon(icon1)
        self.last.setObjectName("last")
        self.horizontalLayout.addWidget(self.last, 0, QtCore.Qt.AlignLeft)
        self.next = QtWidgets.QPushButton(self.centralwidget)
        self.next.setLayoutDirection(QtCore.Qt.RightToLeft)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap(":/icon/next.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.next.setIcon(icon2)
        self.next.setObjectName("next")
        self.horizontalLayout.addWidget(self.next, 0, QtCore.Qt.AlignLeft)
        self.gridLayout.addLayout(self.horizontalLayout, 1, 1, 1, 1)
        self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
        self.treeWidget.setObjectName("treeWidget")
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap(":/icon/catlogs.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.treeWidget.headerItem().setIcon(0, icon3)
        self.gridLayout.addWidget(self.treeWidget, 0, 0, 1, 1, QtCore.Qt.AlignLeft)
        self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
        self.textBrowser.setObjectName("textBrowser")
        self.gridLayout.addWidget(self.textBrowser, 0, 1, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 26))
        self.menubar.setObjectName("menubar")
        self.files = QtWidgets.QMenu(self.menubar)
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap(":/icon/files.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.files.setIcon(icon4)
        self.files.setObjectName("files")
        self.lastfile = QtWidgets.QMenu(self.files)
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap(":/icon/file_last.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.lastfile.setIcon(icon5)
        self.lastfile.setObjectName("lastfile")
        self.setting = QtWidgets.QMenu(self.menubar)
        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap(":/icon/setting.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setting.setIcon(icon6)
        self.setting.setObjectName("setting")
        self.fontcolor = QtWidgets.QMenu(self.setting)
        icon7 = QtGui.QIcon()
        icon7.addPixmap(QtGui.QPixmap(":/icon/fontcolor.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.fontcolor.setIcon(icon7)
        self.fontcolor.setObjectName("fontcolor")
        self.bg = QtWidgets.QMenu(self.setting)
        icon8 = QtGui.QIcon()
        icon8.addPixmap(QtGui.QPixmap(":/icon/bg.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.bg.setIcon(icon8)
        self.bg.setObjectName("bg")
        self.exit = QtWidgets.QMenu(self.menubar)
        icon9 = QtGui.QIcon()
        icon9.addPixmap(QtGui.QPixmap(":/icon/exit.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.exit.setIcon(icon9)
        self.exit.setObjectName("exit")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionfile = QtWidgets.QAction(MainWindow)
        icon10 = QtGui.QIcon()
        icon10.addPixmap(QtGui.QPixmap(":/icon/file.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionfile.setIcon(icon10)
        self.actionfile.setObjectName("actionfile")
        self.action1 = QtWidgets.QAction(MainWindow)
        self.action1.setObjectName("action1")
        self.actionfont = QtWidgets.QAction(MainWindow)
        icon11 = QtGui.QIcon()
        icon11.addPixmap(QtGui.QPixmap(":/icon/font.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionfont.setIcon(icon11)
        self.actionfont.setObjectName("actionfont")
        self.actioncolor = QtWidgets.QAction(MainWindow)
        icon12 = QtGui.QIcon()
        icon12.addPixmap(QtGui.QPixmap(":/icon/color.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actioncolor.setIcon(icon12)
        self.actioncolor.setObjectName("actioncolor")
        self.actionimport = QtWidgets.QAction(MainWindow)
        icon13 = QtGui.QIcon()
        icon13.addPixmap(QtGui.QPixmap(":/icon/import.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionimport.setIcon(icon13)
        self.actionimport.setObjectName("actionimport")
        self.actionclose = QtWidgets.QAction(MainWindow)
        icon14 = QtGui.QIcon()
        icon14.addPixmap(QtGui.QPixmap(":/icon/close.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionclose.setIcon(icon14)
        self.actionclose.setObjectName("actionclose")
        self.actiondefault = QtWidgets.QAction(MainWindow)
        icon15 = QtGui.QIcon()
        icon15.addPixmap(QtGui.QPixmap(":/icon/default.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actiondefault.setIcon(icon15)
        self.actiondefault.setObjectName("actiondefault")
        self.actionexit = QtWidgets.QAction(MainWindow)
        self.actionexit.setIcon(icon9)
        self.actionexit.setObjectName("actionexit")
        self.catlog = QtWidgets.QAction(MainWindow)
        icon16 = QtGui.QIcon()
        icon16.addPixmap(QtGui.QPixmap(":/icon/catlog.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.catlog.setIcon(icon16)
        self.catlog.setObjectName("catlog")
        self.files.addAction(self.actionfile)
        self.files.addAction(self.lastfile.menuAction())
        self.fontcolor.addAction(self.actionfont)
        self.fontcolor.addAction(self.actioncolor)
        self.bg.addAction(self.actionimport)
        self.bg.addAction(self.actionclose)
        self.setting.addAction(self.fontcolor.menuAction())
        self.setting.addAction(self.bg.menuAction())
        self.setting.addAction(self.actiondefault)
        self.exit.addAction(self.actionexit)
        self.menubar.addAction(self.files.menuAction())
        self.menubar.addAction(self.setting.menuAction())
        self.menubar.addAction(self.exit.menuAction())
        self.toolBar.addAction(self.catlog)

        self.retranslateUi(MainWindow)
        self.actionexit.triggered.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "TXT阅读器"))
        MainWindow.setStatusTip(_translate("MainWindow", "TXT阅读器"))
        self.last.setToolTip(_translate("MainWindow", "上一章"))
        self.last.setStatusTip(_translate("MainWindow", "上一章"))
        self.last.setText(_translate("MainWindow", "上一章"))
        self.next.setToolTip(_translate("MainWindow", "下一章"))
        self.next.setStatusTip(_translate("MainWindow", "下一章"))
        self.next.setText(_translate("MainWindow", "下一章"))
        self.treeWidget.setToolTip(_translate("MainWindow", "章节目录"))
        self.treeWidget.setStatusTip(_translate("MainWindow", "章节目录"))
        self.treeWidget.headerItem().setText(0, _translate("MainWindow", "章节目录"))
        self.files.setStatusTip(_translate("MainWindow", "文件"))
        self.files.setTitle(_translate("MainWindow", "文件"))
        self.lastfile.setToolTip(_translate("MainWindow", "打开最近的文件"))
        self.lastfile.setStatusTip(_translate("MainWindow", "打开最近的文件"))
        self.lastfile.setTitle(_translate("MainWindow", "打开最近的文件"))
        self.setting.setToolTip(_translate("MainWindow", "设置"))
        self.setting.setStatusTip(_translate("MainWindow", "设置"))
        self.setting.setTitle(_translate("MainWindow", "设置"))
        self.fontcolor.setTitle(_translate("MainWindow", "字体和颜色"))
        self.bg.setTitle(_translate("MainWindow", "背景图片"))
        self.exit.setToolTip(_translate("MainWindow", "关闭应用"))
        self.exit.setStatusTip(_translate("MainWindow", "关闭应用"))
        self.exit.setTitle(_translate("MainWindow", "退出"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionfile.setText(_translate("MainWindow", "打开文件"))
        self.actionfile.setStatusTip(_translate("MainWindow", "打开文件"))
        self.actionfile.setShortcut(_translate("MainWindow", "Ctrl+O"))
        self.action1.setText(_translate("MainWindow", "1"))
        self.actionfont.setText(_translate("MainWindow", "选择字体"))
        self.actionfont.setStatusTip(_translate("MainWindow", "选择字体"))
        self.actioncolor.setText(_translate("MainWindow", "选择背景颜色"))
        self.actioncolor.setStatusTip(_translate("MainWindow", "选择背景颜色"))
        self.actionimport.setText(_translate("MainWindow", "导入背景图片"))
        self.actionimport.setStatusTip(_translate("MainWindow", "导入背景图片"))
        self.actionclose.setText(_translate("MainWindow", "关闭背景图片"))
        self.actionclose.setStatusTip(_translate("MainWindow", "关闭背景图片"))
        self.actiondefault.setText(_translate("MainWindow", "恢复默认设置"))
        self.actiondefault.setStatusTip(_translate("MainWindow", "恢复默认设置"))
        self.actionexit.setText(_translate("MainWindow", "退出应用"))
        self.actionexit.setStatusTip(_translate("MainWindow", "退出应用"))
        self.actionexit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
        self.catlog.setText(_translate("MainWindow", "目录"))
        self.catlog.setToolTip(_translate("MainWindow", "目录"))
import resource_rc

配置文件。

[DEFAULT]
file=
files=
chapter=0

[FILE]
file=
files=
chapter=0

完整代码。

import ctypes
import re
import sys

import cchardet as cchardet
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QMessageBox, QTreeWidgetItem

from QtDesigner.UI.UIReader import Ui_MainWindow

# 加上这段话,在运行程序时,设置的窗口图标才会出现
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")

class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)  # 初始化父类
        self.setupUi(self)  # 继承 Ui_MainWindow 界面类
        # 加载设置
        self.init_info()
        # 开局先隐藏按钮
        self.last.setHidden(True)
        self.next.setHidden(True)
        # 设置目录栏初始为隐藏状态
        self.treeWidget.setHidden(True)
        # 如果之前打开过文件,则直接加载文件
        if self.cur_file:
            self.load_file(self.cur_file)
        # 打开文件
        self.actionfile.triggered.connect(self.open_file)
        # 上一章,下一章按钮点击事件
        self.last.clicked.connect(self.show_last)
        self.next.clicked.connect(self.show_next)
        # 点击目录显示/隐藏目录
        self.catlog.triggered.connect(self.tab_catlog)

    # 显示/隐藏目录
    def tab_catlog(self):
        if self.treeWidget.isVisible():
            self.treeWidget.setHidden(True)
        else:
            self.treeWidget.setVisible(True)

    # 打开文件
    def open_file(self):
        # 弹出QFileDialog窗口。getOpenFileName()方法的第一个参数是说明文字,
        # 第二个参数是默认打开的文件夹路径。默认情况下显示所有类型的文件。
        if not self.cur_file:
            path = '/'
        else:
            path = self.cur_file
        fname = QFileDialog.getOpenFileName(self, '打开文件', path, filter='*.txt')
        self.load_file(fname[0])

    # 显示最近的文件
    def show_last_file(self):
        # 每次绘制时将之前的清空
        self.lastfile.clear()
        _translate = QtCore.QCoreApplication.translate
        for i, file in enumerate(self.last_files):
            # 截取文件名
            name = file.split('/')[-1].split('.')[0]
            # 添加action
            action = QtWidgets.QAction(self)
            action.setObjectName(f'file{i}')    # 设置对象名
            self.lastfile.addAction(action)  # 添加到菜单栏中
            action.setText(_translate("MyMainWindow", name))    # 添加到主窗口,且设置text
            action.triggered.connect(self.open_last_file)   # 设置触发事件

    # 打开最近的文件
    def open_last_file(self):
        sender = self.sender().objectName()  # 获取当前信号 sender
        # 根据我们设置的对象名,截取字符,然后从配置文件寻找文件路径
        self.load_file(self.last_files[int(sender[-1])])

    def load_file(self, file):
        # 文件不为空
        if file:
            try:
            	# 打开新文件时重置章节
                if not self.last_files or self.cur_file != self.last_files[-1]:
                    self.chapter = 0
                # 更改目前打开的文件
                self.cur_file = file
                self.filename = file.split('/')[-1].split('.')[0]
                # 将打开的文件添加到最近文件中去
                # 如果文件存在,则要更改文件的打开顺序
                if file in self.last_files:
                    self.last_files.remove(file)
                self.last_files.append(file)
                # 只存储最近打开的五本书籍
                if len(self.last_files) > 5:
                    self.last_files.pop(0)
                # 获取文件的编码格式
                encodings = self.get_encoding(file)
                with open(file, 'r', encoding=encodings) as f:
                    # 打开文件,生成章节目录
                    self.chapters = []
                    # 包含了txt文本的全部内容
                    self.lines = f.readlines()
                    # 一种匹配章节目录的规则
                    pattern = r"(第)([\u4e00-\u9fa5a-zA-Z0-9]{1,7})[章|节][^\n]{1,35}(|\n)"
                    for i in range(len(self.lines)):
                        line = self.lines[i].strip()
                        if line != "" and re.match(pattern, line):
                            line = line.replace("\n", "").replace("=", "")
                            if len(line) < 30:
                                self.chapters.append({line: i})
                 # 如果没有可用的目录,那就显示全部
                if not self.chapters:
                    self.chapters.append({self.filename: 0})
                # print(self.chapters)
                # 显示最近打开的文件
                self.show_last_file()
                # 设置章节目录
                self.setChapters()
                # 设置文本浏览器的内容
                self.show_content()
            except:
                self.show_msg('文件不存在或者错误!')
        else:   # 文件为空,说明没有选择文件
            self.show_msg('您没有选择文件!')

    # 设置章节目录
    def setChapters(self):
        # 每次绘制目录时先清除一下
        self.treeWidget.clear()
        _translate = QtCore.QCoreApplication.translate
        __sortingEnabled = self.treeWidget.isSortingEnabled()
        for i, value in enumerate(self.chapters):
            item = QTreeWidgetItem(self.treeWidget)
            item.setText(0, _translate("MyMainWindow", list(value.keys())[0]))
            self.treeWidget.addTopLevelItem(item)
        self.treeWidget.setSortingEnabled(__sortingEnabled)
        self.treeWidget.clicked.connect(self.onTreeClicked)
        self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(self.chapter),0)
        # 为当前章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))

    # 点击目录跳转到章节
    def onTreeClicked(self, index):
        # 恢复原来章节的背景色(设置透明度为0),为新章节设置背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0,0,0, 0))
        # 获取点击的项目下标
        self.chapter = int(index.row())
        # 判断按钮是否要显示
        self.show_button()
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15,136,235))
        self.show_content()


    # 展示上一章
    def show_last(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter - 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))

    # 展示下一章
    def show_next(self):
        # 更改目录背景色
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(0, 0, 0, 0))
        self.chapter = self.chapter + 1
        self.show_content() # 显示内容
        self.treeWidget.topLevelItem(self.chapter).setBackground(0, QColor(15, 136, 235))

    # 设置文本浏览器的内容
    def show_content(self):
        # 在展示内容时直接判断按钮是否要隐藏
        self.show_button()
        # 将文件内容添加到文本浏览器中
        self.textBrowser.setText(self.get_content())
        # 状态栏显示当前的章节内容和目录名
        self.textBrowser.setStatusTip(self.filename + "   " +list(self.chapters[self.chapter].keys())[0])

    # 获取章节内容
    def get_content(self):
        index = self.chapter
        # 起始行
        start = list(self.chapters[index].values())[0]
        # 如果是终章
        if index == self.treeWidget.topLevelItemCount() - 1:
            return "".join(self.lines[start:-1])
        else:
            # 终止行
            end = list(self.chapters[index + 1].values())[0]
            return "".join(self.lines[start:end])

    # 根据显示的章节来决定按钮是否要改动,简单一点,直接隐藏按钮
    def show_button(self):
        # 考虑只有一个章节的情况
        if len(self.chapters) == 1:
            self.last.setHidden(True)
            self.next.setHidden(True)
        # 第一章
        elif self.chapter == 0:
            self.last.setHidden(True)
            self.next.setVisible(True)
         # 末章
        elif self.chapter == len(self.chapters) - 1:
            self.last.setVisible(True)
            self.next.setHidden(True)
        # 其他情况,恢复按钮
        else:
            if self.last.isHidden():
                self.last.setVisible(True)
            if self.next.isHidden():
                self.next.setVisible(True)

    def show_msg(self, msg):
        # 后两项分别为按钮(以|隔开,共有7种按钮类型,见示例后)、默认按钮(省略则默认为第一个按钮)
        reply = QMessageBox.information(self, "提示", msg, QMessageBox.Yes | QMessageBox.No,
                                        QMessageBox.Yes)

    # 获取文件编码类型
    def get_encoding(self, file):
        # 二进制方式读取,获取字节数据,检测类型
        with open(file, 'rb') as f:
            return cchardet.detect(f.read())['encoding']

    # 初始化设置
    def init_info(self):
        self.setting = QtCore.QSettings("./config.ini", QtCore.QSettings.IniFormat)   # 配置文件
        self.setting.setIniCodec('utf-8')   # 设置配置文件的编码格式
        self.cur_file = self.setting.value("FILE/file")     # 目前打开的文件
        self.last_files = self.setting.value("FILE/files")  # 最近打开的文件
        if not self.last_files:
            self.last_files = []
        self.chapter = int(self.setting.value("FILE/chapter"))  # 上次浏览的章节
    # 保存设置
    def save_info(self):
        self.setting.setValue("FILE/file", self.cur_file)
        self.setting.setValue("FILE/files", self.last_files)
        self.setting.setValue("FILE/chapter", self.chapter)

    # 关闭窗口时更新窗口大小
    def closeEvent(self, event):
        result = QMessageBox.question(self, "关闭应用", "确定关闭应用?",
                                      QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if result == QMessageBox.Yes:
            # 在关闭窗口时保存设置
            self.save_info()
            event.accept()
            QMainWindow.closeEvent(self, event)
        else:
            event.ignore()

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序

下期我们为设置的菜单栏添加动作。

标签:last,QtGui,Python,self,pyqt5,-------,file,translate,MainWindow
From: https://www.cnblogs.com/pikeduo/p/17037173.html

相关文章

  • 使用Python库pyqt5制作TXT阅读器(二)-------打开文件和浏览最近的文件
    项目地址:https://github.com/pikeduo/TXTReaderPyQt5中文手册:https://maicss.gitbook.io/pyqt-chinese-tutoral/pyqt5/QtDesigner学习地址:https://youcans.blog.csdn.net......
  • linux--第一课
    主题:linux扫盲(简单)今日内容概要linux岗位需求linux工作本质linux简要发展史服务器的种类及组成部分虚拟化技术虚拟化软件及相关配置今日内容详细linux常见......
  • DML-(Data Manipulation Language)
    DML(DataManipulationLanguage):数据操作语言,用来对数据库中表的数据记录进行增、删、改操作。添加数据(insert)修改数据(update)删除数据(delete)添加数据给指定字段添加......
  • Model-Agnostic Meta-Learning (MAML)模型介绍及算法详解
    paper:​​​https://link.zhihu.com/?target=https%3A//arxiv.org/pdf/1703.03400.pdf​​ MAML在学术界已经是非常重要的模型了,论文Model-AgnosticMeta-LearningforFa......
  • 算法--2023.1.9
    1.力扣128-最长连续序列classSolution{publicintlongestConsecutive(int[]nums){//通过hashset保存去重复后的所有数据intn=nums.lengt......
  • Python安装libsvm
    最近在学习机器学习,听的是浙大胡浩基老师的课。胡老师用的是MatLab,我平时用Python比较多,在网上找Python安装libsvm的教程发现比较少,花了几个小时终于成功安装了libsvm,现......
  • CSS - 动画,动画的定义,动画全部属性
    1.动画的定义<style>/*只定义起始与结束*/@keyframes动画名称1{from{}to{}}/*用百分比定义动画*/@keyframes动画......
  • GO语言的进程管理工具-实践
    GO编写的进程管理工具Gosuv Gosuv是一个进程管理工具,使用Go语言编写。与python-supervisor的功能和用法类似。一、Gosuv的特点:Web控制台部分做了重点加强,因为......
  • CSS - 2D转换,移动,旋转,缩放
    1.移动/*第一个参数控制左右,第二个参数控制上下*/transform:translate(10px,20px);/*单独控制左右*/transform:translateX(10px);/*单独控制上下*/transfor......
  • 修改时间服务器-域控环境
    首先当前大部分公司都采用了域控环境,意味着在时间和日期那里是看不到windows时间选项的。此时最好通过域控策略或者单纯的dos命令来更改域计算机时间同步服务器信息一......