鱼C论坛

 找回密码
 立即注册
查看: 1410|回复: 10

[作品展示] 【源码】MarDisTextConverter 源文本转换器

[复制链接]
发表于 2023-4-23 01:37:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Twilight6 于 2023-4-23 11:36 编辑

MarDisTextConverter 源码

                               
登录/注册后可看大图

                               
登录/注册后可看大图

(温馨提示:要使程序正常运行,需要按照如下项目结构进行配置哦~)

本贴是源码贴,源码过长,所以新建帖子来进行引用。
如果感兴趣请访问:【源文本转换器】Markdown ←→ Discuz 源文本相互转换器,欢迎兄弟们捧场!

[b]
Ps: 源码很杂乱,仅供参考,不建议学习...
若拷贝此帖子源码,需要将 s 、code 标签后面的空格删除,否则可能会影响转换(拷贝需要修改,源码无需改动

项目结构




                                                                                                                                                                                        MarDisTextConverter/
                                                                                                                                                                                        │
                                                                                                                                                                                        ├── assets/
                                                                                                                                                                                        │        └── img
                                                                                                                                                                                        │                └── *.png
                                                                                                                                                                                        │
                                                                                                                                                                                        ├──  config/
                                                                                                                                                                                        │        ├── config.ini
                                                                                                                                                                                        │        └──ConfigReader.py
                                                                                                                                                                                        │
                                                                                                                                                                                        ├── converter/
                                                                                                                                                                                        │        └──TextConverter.py
                                                                                                                                                                                        │
                                                                                                                                                                                        ├── ui/
                                                                                                                                                                                        │        └── TextGUI.py
                                                                                                                                                                                        │
                                                                                                                                                                                        ├── main.png
                                                                                                                                                                                        ├── main.py
                                                                                                                                                                                        ├── README.md
                                                                                                                                                                                        └── UangSC

项目源码



源码打包资源下载:
游客,如果您要查看本帖隐藏内容请回复

Ps:免费和付费的是同样的源码都一样,希望大家能支持一下购买附件啦~

TextConverter.py

                               
登录/注册后可看大图
import os
import re
from uuid import uuid4
from chardet import detect

from config.ConfigReader import ConfigReader


class TextConverter:
    config_reader = None
    path = os.getcwd()
    DToMDict = {}
    TitleMaxSize = 7
    TitleBold = 1
    EnglishDefaultFont = "Times New Roman"
    # 部分配置文件默认值与参数类型
    ConfigDict = {
        "TitleMaxSize": (int, 7, 5, 7),
        "TitleBold": (int, 1, 0, 1),
        "EnglishDefaultFont": (str, "Times New Roman")
    }
    _ConfigureBackup = {
        'icons': {'icon': r'/assets/img/icon.png', 'about': r'/assets/img/about.png', 'convert': r'/assets/img/convert.png',
                  'exit': r'/assets/img/exit.png', 'help': r'/assets/img/help.png', 'new': r'/assets/img/new.png',
                  'maximize': r'/assets/img/maximize.png', 'minimize': r'/assets/img/minimize.png',
                  'open': r'/assets/img/open.png', 'restore': r'/assets/img/restore.png', 'save': r'/assets/img/save.png'},
        'styles': {'QButton': 'QToolButton,QPushButton{padding-top: 5px; padding-bottom: 5px;}',
                   'QToolBar': 'QToolBar{background-color: #82B7DB; spacing: 15px;}',
                   'textEditBg': 'background-color:#CBDCE9;'},
        'fonts': {'TitleMaxSize': '7', 'TitleBold': '1', 'EnglishDefaultFont': 'Times New Roman'},
        'settings': {'FixedSize': '1'}, 'files': {'RecentFile': '[]'}
    }
    _DefaultConfig = ['styles', 'fonts', 'settings']

    @classmethod
    def _loadConfig(cls, flag = False):
        if cls.config_reader == None or flag:
            cls.config_reader = ConfigReader(cls.path + "\config\config.ini")
            for confName in cls.ConfigDict:
                confValue = cls.config_reader.get_fonts(confName)
                cls._configToType(confValue, confName)
            cls._dToMDict()

    @classmethod
    def _configToType(cls, value, name):
        try:
            typeMethod = cls.ConfigDict[name]
            value = typeMethod[0](value)
            if typeMethod[0] == int:
                value = value if value >= typeMethod[2] and value <= typeMethod[3] else typeMethod[1]
            setattr(TextConverter, name, value)

        except Exception as e:
            pass

    @classmethod
    def _dToMDict(cls):
        for i in range(0, 4):
            cls.DToMDict[str(cls.TitleMaxSize - i)] = "#" * (i + 1)
        cls.DToMDict["Min"] = ""

    @classmethod
    def discuzToMarkdown(cls, text: str) -> str:
        cls._loadConfig()
        def _quote(text):
            text = re.sub(r'\n*(\[quote\].*?\[/quote\])\n*', "\g<1>", text, flags=re.S)
            text = re.sub(r'(\[quote\])(.*?)(\[/quote\])', lambda m: m.group(1) + "\n" + m.group(2).strip() + "\n" + m.group(3), text,
                          flags=re.S)
            text = re.sub(r'(\[/quote\])(\[quote\])', '\g<1>\n\g<2>', text, flags=re.S)
            text = re.sub(r'((?<!\n)\[quote\])|(\[/quote\](?!\n))',
                          lambda m: "\n" + m.group(1) if m.group(1) != None else m.group(2) + "\n", text, flags=re.S)
            text = re.sub(r'\[quote\](.*?)\n\[/quote\]', lambda m: m.group(1).replace("\n", "\n> "),text, flags=re.S)
            return text

        text, dict_code = cls._replace(r"\[code\].*?\[/code\]", text, re.S)
        calcTitle = lambda m: (cls.DToMDict[
            str(cls.TitleMaxSize) if int(m.group(1)) > cls.TitleMaxSize else "Min" if (
                    int(m.group(1)) <= cls.TitleMaxSize - 4) else str(m.group(1))]) + " " + m.group(4)
        tag_list = {
            r'(\(.*?)\[/font\])': r'\g<2>',
            r'\[size=(\d+)\]((\[b\])?(.*?)(\[/b\])?)\[/size\]': calcTitle,
            r'\[backcolor=.*?\]\[color=.*?\]\[b\](.+?)\[/b\]\[/color\]\[/backcolor\]': r'`\g<1>`',
            r'\[b\](.*?)\[\/b\]': r'**\1**',
            r'\[i\](.*?)\[\/i\]': r'*\1*',
            r'\[u\](.*?)\[\/u\]': r'<u>\1</u>',
            r'\[align=center\](.*?)\[\/align\]': r'<center>\1</center>',
            r'\[hr\]': '---',
            r'\[s\](.*?)\[\/s\]': r'~~\1~~',
            r'\(.*?)\[/url\]': r'[\2](\1)',
            r'\[code\]\n*([\s\S]*?)\n*\[\/code\]': r'```\n\1\n```'
        }

        for pattern, replace in tag_list.items():
            if "font" in pattern:
                text = re.sub(pattern, replace, text)
            elif "code" in pattern:
                text = cls._recovery(dict_code, text)
                text = re.sub(pattern, replace, text)
            else:
                text = re.sub(pattern, replace, text, flags=re.DOTALL)
        text = _quote(text)
        return text.strip()

    @classmethod
    def markdownToDiscuz(cls, text: str) -> str:
        cls._loadConfig()

        def _quote_to_Discuz(text):
            result, temp, new_text = [], [], []
            for i in text.splitlines():
                if i[0:1] != ">":
                    if temp != []:
                        new_text.append("[quote]" + "\n".join(temp) + "[/quote]")
                        temp = []
                    new_text.append(i)
                    result.append(i)
                else:
                    temp.append(i.strip()[2 if i[1:2] == " " else 1:])
            if temp != []:
                new_text.append("[quote]" + "\n".join(temp) + "[/quote]")
            return "\n".join(new_text)

        b_start, b_end = ("[b]", "[/b]") if cls.TitleBold else ("", "")
        text = re.sub(r'```\n*(.*?)\n*```', r'[code]\n\1\n[/code ]', text, flags=re.DOTALL)
        text, dict_code = cls._replace(r"\[code\].*?\[/code\]", text, re.S)
        tag_list = {
            r'\[(.*?)\]\((http[s ]?://[^\s\[\]]+)\)': r'[url=\2]\1',
            r'((\n#+)\s*(.*))|(^(#+)\s*(.*))': lambda
                m: ("" if m.group(1) == None else "\n") + rf"[size={(cls.TitleMaxSize - m.group(0).count('#') + 1) if (cls.TitleMaxSize - m.group(0).count('#') + 1) > 0 else 1}]{b_start + (m.group(3) if m.group(3) != None else m.group(6)) + b_end}",
            r'\*\*(.*?)\*\*': r'[b]\1[/b]',
            r'\*(.*?)\*': r'[i]\1[/i]',
            r'<u>(.*?)</u>': r'[u]\1[/u]',
            r'<center>(.*?)</center>': r'
\1
',             r'---\n': '[hr]\n',             r'~~(.*?)~~': r'[s ]\1[/s]',             r'```\n*(.*?)\n*```': r'[code]\n\1\n[//code]', # 此处若有两个 // 则需要删去一个             r"`(.+?)`": r"[b]\g<1>[/b]"         }         for pattern, replace in tag_list.items():             if "#" in pattern or "http" in pattern:                 text = re.sub(pattern, replace, text)             else:                 text = re.sub(pattern, replace, text, flags=re.DOTALL)         # 引用匹配转换         text = _quote_to_Discuz(text)         # [url]、普通 url 临时替换         text, dict_url = cls._replace(             r"\[url=?https?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]*[-A-Za-z0-9+&@#\/%=~_|](?=(?:[^`]*`[^`]*`)*[^`]*$)\].*\[/url\]",             text)         text, dict_url2 = cls._replace(             r"https?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]*[-A-Za-z0-9+&@#\/%=~_|](?=(?:[^`]*`[^`]*`)*[^`]*$)", text)         # 英文将使用 Times New Roman 字体         text = re.sub(r"[a-zA-Z]+(?![^\[]*\])", f"\g<0>", text)         text = cls._recovery(dict_code, text)         text = cls._recovery(dict_url, text)         text = cls._recovery(dict_url2, text)         return text.strip()     @staticmethod     def _replace(pattern, text, flags=0):         codeDict = {}         while data := re.search(pattern, text, flags):             id = str(uuid4())             text = re.sub(pattern, f"[{id}]", text, count=1, flags=flags)             codeDict[id] = data.group(0)         return text, codeDict     @staticmethod     def _recovery(codeDict, text):         for i, j in codeDict.items():             text = re.sub("\[" + i + "\]", j, text, count=1)         return text     @staticmethod     def _getFileEncoding(file):         with open(file, "rb") as f:             result = detect(f.read())             encoding = result["encoding"]         if encoding:             return encoding         return "utf-8"     # 暂未实现 -> 文本转换时检测类型     @classmethod     def _checkType(cls, text):         pass


TextGUI.py

                               
登录/注册后可看大图
from os.path import split, isfile, samefile, exists
from time import sleep

import chardet
from PyQt5.QtWidgets import QMainWindow, QAction, QTextEdit, QFileDialog, QMessageBox, QDesktopWidget, \
    QComboBox, QWidget, QHBoxLayout, QLabel, QStatusBar, QApplication
from PyQt5.QtGui import QIcon, QColor, QFont, QPixmap, QDesktopServices
from PyQt5.QtCore import Qt, QUrl, QSize, pyqtSignal, QThread

from config.ConfigReader import ConfigReader
from converter.TextConverter import TextConverter as Tc


class MyTextEdit(QMainWindow):
    _version = "v0.1.1"
    def __init__(self, exePath, log):
        super().__init__()

        self._log = log
        self._exe = exePath
        path, _ = split(exePath)
        self.config_path = path + r"\config\config.ini"
        self.config_reader = ConfigReader(self.config_path)
        self._setImg(path)
        try:
            self.FixedSize = int(self.config_reader.get_settings("FixedSize"))
            self.RecentFile = eval(self.config_reader.get_files("RecentFile"))
        except Exception:
            self.FixedSize = 1
            self.RecentFile = []
            self._log.exception(Exception("配置文件异常,部分属性已使用默认值!"))
        self.currentFilePath = ""
        self.msg = ""
        self.error = self.first = 1

        # 设置窗口标题和图标
        self.setWindowTitle("MarDisTextConverter——源文本转换编辑器          ♥~\\(≧v≦*~\\)0Oo。")
        icon = QIcon(self._Icon)
        self.setWindowIcon(icon)
        self.windowIcon().addFile("icon.png", QSize(48, 48))

        # 设置窗口大小和位置
        self.setGeometry(300, 200, 820, 760)
        if self.FixedSize:
            self.setFixedSize(820, 760)
        # 透明度
        self.setWindowOpacity(0.95)
        self.center()
        # 添加工具栏和按钮
        toolbar = self.addToolBar("主工具栏")

        # 取消默认主工具栏选项
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(lambda: None)

        # 设置撤回组件
        undoAction = QAction(self)
        undoAction.setShortcut('Ctrl+Z')
        self.addAction(undoAction)

        # 添加菜单栏
        self.menubar = self.menuBar()

        file_menu = self.menubar.addMenu("文件")
        self._addQAcion(file_menu, "新建文件", "", "快捷键:Ctrl+N", "Ctrl+N" ,self.createFile)
        self._addQAcion(file_menu, "打开", "", "快捷键:Ctrl+O", "Ctrl+O" ,self.openFile)
        self._addQAcion(file_menu, "保存", key="Ctrl+S", slot=self.saveCurrentFile)
        self._addQAcion(file_menu, "文件另存为...", key="Ctrl+Shift+S", slot= lambda : self.saveFile(1))
        self.recent_files_menu = file_menu.addMenu('最近打开的文件')
        if self.RecentFile:
            self.updateRecent()

        edit_menu = self.menubar.addMenu("编辑")
        self.textEdit = QTextEdit()
        self.textEdit.textChanged.connect(self.calculate_char_count)
        self._addQAcion(edit_menu, "撤回", key="Ctrl+Z", slot=self.textEdit.undo)
        self._addQAcion(edit_menu, "复制", key="Ctrl+C", slot=self.textEdit.copy)
        self._addQAcion(edit_menu, "剪切", key="Ctrl+X", slot=self.textEdit.cut)
        self._addQAcion(edit_menu, "黏贴", key="Ctrl+V", slot=self.textEdit.paste)
        self._addQAcion(edit_menu, "清空文本框", key="Ctrl+L", slot=self._clear)

        self.copypath = QAction("拷贝文件路径", self)
        self.copypath.triggered.connect(self.copyPath)
        self.copypath.setEnabled(False)
        edit_menu.addAction(self.copypath)

        setup_menu = self.menubar.addMenu("设置")
        self._addQAcion(setup_menu, "打开配置文件", key="Ctrl+Alt+S", slot=lambda: self.openFile(self.config_path))
        self._addQAcion(setup_menu, "恢复默认设置", slot=self._restoreDefaults)

        self.widget = QWidget(self)
        layout = QHBoxLayout(self.widget)
        self.menuRLable = QLabel(" " * 120)
        self.menuRLable.setAlignment(Qt.AlignRight)
        layout.addWidget(self.menuRLable)
        layout.setContentsMargins(5, 3, 5, 0)
        self.widget.setLayout(layout)

        self.updateT = self.WorkerThread()
        self.updateT.update_signal.connect(self._outputText)
        self.updateT.start()

        # 将包含文本标签的布局添加到菜单栏的角
        self.menubar.setCornerWidget(self.widget)

        # 工具栏各按钮添加、描述并设置快捷键
        self.convertComboBox = QComboBox(self)
        self.convertComboBox.addItem("Markdown 转 Discuz")
        self.convertComboBox.addItem("Discuz 转 Markdown")
        self.convertComboBox.setCurrentIndex(0)
        self._addQAcion(key="Ctrl+Shift+D", slot=self.MarToDis)
        self._addQAcion(key="Ctrl+Shift+M", slot=self.DisToMar)
        self.convertComboBox.setToolTip("转换格式")
        toolbar.addWidget(self.convertComboBox)
        self._addQAcion(toolbar, "打开", self._Open, "打开文件(Ctrl+O)", "Ctrl+O", self.openFile)
        self._addQAcion(toolbar,"保存", self._Save, "保存文件(快捷键:Ctrl+S)", "",
                        self.saveCurrentFile)
        self._addQAcion(toolbar,"转换", self._Convert, "转换格式(快捷键:Ctrl+T)", "Ctrl+T",
                        self.convert)
        self._addQAcion(toolbar,"帮助", self._Help, "打开帮助页面(快捷键:Ctrl+H)", "Ctrl+H",
                        self.showHelp)
        self._addQAcion(toolbar,"关于", self._About, "更多信息", slot=self.showAbout)
        self._addQAcion(toolbar,"最小化", self._Minimize, "最小化窗口(快捷键:Ctrl+M)", "Ctrl+M",
                        self.minimizeWindow)
        self.maxButton = self._addQAcion(toolbar,"最大化", self._Maximize, "最大/恢复化窗口(快捷键:Ctrl+D)", "Ctrl+D",
                        self.showMaximizedOrRestore)
        self._addQAcion(toolbar,"退出程序", self._Exit, "退出应用程序(Ctrl+Q)", "Ctrl+Q",
                        self.close)

        if self.FixedSize:
            self.maxButton.setEnabled(False)

        # 设置工具栏按钮占满整个工具栏
        toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        toolbar.setStyleSheet(self.config_reader.get_style("QButton"))
        toolbar.setStyleSheet(self.config_reader.get_style("QToolBar"))

        # 设置窗口颜色主题
        palette = self.palette()
        palette.setColor(palette.Window, QColor(130, 183, 219))
        self.setPalette(palette)

        # 设置文本编辑框
        self.textEdit.setAcceptRichText(False)
        # 调整字体大小
        self.textEdit.setFont(QFont("Microsoft YaHei", 12))

        self.setCentralWidget(self.textEdit)
        # 设置编辑器背景颜色
        self.textEdit.setStyleSheet(self.config_reader.get_style("textEditBg"))

        # 创建一个状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.count_label = QLabel(f'共计:0 行  字符个数为:0', self.status_bar)
        self.count_label.setAlignment(Qt.AlignRight)
        self.status_bar.addWidget(self.count_label)
        self.status_bar.addPermanentWidget(self.count_label)
        self.file_label = QLabel("<b>当前暂未打开文件...</b>")
        self.file_label.setAlignment(Qt.AlignCenter)
        self.status_bar.addWidget(self.file_label)

    class WorkerThread(QThread):
        update_signal = pyqtSignal(str)  # 自定义信号

        def __init__(self):
            super().__init__()
            self.text = ""
            self.idx = 1

        def run(self):
            while True:
                if self.idx < len(self.text) + 1:
                    self.update_signal.emit(self.text[:self.idx])
                    self.idx += 1
                sleep(0.04)

        def setText(self, text):
            self.idx = 1
            self.text = text

    # 读取配置文件获取图片路径
    def _setImg(self, path):
        sections = self.config_reader.config._sections["icons"]
        for k in sections:
            setattr(MyTextEdit, "_" + k[:1].upper() + k[1:], path + sections[k])

    def _clear(self):
        self.textEdit.selectAll()
        self.textEdit.textCursor().removeSelectedText()
    def MarToDis(self):
        self.convertComboBox.setCurrentIndex(0)
        self.convert()

    def DisToMar(self):
        self.convertComboBox.setCurrentIndex(1)
        self.convert()

    # 创建新文件
    def createFile(self):
        msgBox = QMessageBox(self)
        icon = QIcon(self._New)
        msgBox.setIconPixmap(icon.pixmap(48, 48))
        msgBox.setWindowIcon(QIcon(self._Icon))
        msgBox.setWindowTitle("创建新文件")
        msgBox.setText("在新窗口中创建并打开新文件,还是本窗口?")
        msgBox.addButton("新窗口", QMessageBox.YesRole)
        msgBox.addButton("本窗口", QMessageBox.NoRole).setFocus()
        msgBox.addButton("取消", QMessageBox.RejectRole)
        isNew = msgBox.exec_()
        if isNew == 0:
            self.updateT.setText("Biu~</b> (*/ω \*)嘿嘿,又冒出一个我~")
            QDesktopServices.openUrl(QUrl.fromLocalFile(self._exe))
        elif isNew == 1:
            self.textEdit.selectAll()
            self.textEdit.textCursor().removeSelectedText()
            self.currentFilePath = ""
            self.file_label.setText(f" <b>新建文件</b>,暂未保存... ")
            self.updateT.setText("ψ(._.  )、 嘿咻~</b> 我帮你清空了文本框的文字啦~ ")
        else:
            self.updateT.setText("(*^-^*)</b> 你取消了新文件的创建哦~ ")

    # 统计文本数据信息
    def calculate_char_count(self):
        text = self.textEdit.toPlainText()
        char_count = len(text)
        doc = self.textEdit.document()
        line_count = doc.lineCount()
        self.count_label.setText(f'共计: <b>{line_count}</b> 行  字符个数为:<b>{char_count}</b>')

    # 更新左下角当前打开的文件
    def update_file_label(self, fileName):
        self.file_label.setText(f"当前文件:<b>{fileName}</b>")
    # 恢复默认设置
    def _restoreDefaults(self):
        reply = QMessageBox.question(
            self, '是否恢复默认配置?...(*°▽°*)~!',
            '此操作会将配置恢复为<span style="color:red; font-weight:bold;">默认配置</span>!',
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.config_reader.restore = 1
            self.config_reader.save_Config(Tc._ConfigureBackup)
            Tc._loadConfig(True)
            self.openFile(self.config_path, True)
            self.config_reader.restore = 0
        else:
            self.updateT.setText("(*/ω\*)</b>¥@!呼....好可怕,差点...失忆!")

    # 移动窗口屏幕中央
    def center(self):
        screen = QDesktopWidget().screenGeometry()
        x = (screen.width() - self.width()) // 2
        y = (screen.height() - self.height()) // 2
        self.move(x, y)


    # 添加工具栏按键
    def _addQAcion(self, module=None, name="", img_path="", tip="", key=None, slot=None):
        if img_path:
            action = QAction(QIcon(img_path), name, self)
        else:
            action = QAction(name, self)
        action.setToolTip(tip) if tip else None
        action.setShortcut(key) if key else None
        action.triggered.connect(slot)
        if module != None:
            module.addAction(action)
        else:
            self.addAction(action)
        return action

    # 拷贝文件路径
    def copyPath(self):
        QApplication.clipboard().setText(self.currentFilePath)

    # 添加到最近文件列表
    def addRecent(self, path):
        if path in self.RecentFile:
            self.RecentFile.remove(path)
        elif len(self.RecentFile) == 5:
            self.RecentFile.pop()
        self.RecentFile.insert(0, path)
        self.updateRecent()

    # 更新最近文件列表
    def updateRecent(self):
        self.recent_files_menu.clear()
        for i in range(len(self.RecentFile)):
            file_action = self.recent_files_menu.addAction(f"{i + 1}. {split(self.RecentFile[i])[1]}")
            file_action.triggered.connect(self.open_recent_file)
        self.config_reader.update_recentFile(self.RecentFile)

    # 打开最近文件
    def open_recent_file(self):
        file = self.sender().text()
        idx, fileName = file.split(". ", 1)
        filePath = self.RecentFile[int(idx) - 1]
        self.openFile(filePath)

    # 打开文件
    def openFile(self, filePath="", confmsg=False):
        temp = False
        flag = self.msg
        try:
            if not filePath:
                filePath, _ = QFileDialog.getOpenFileName(self, "选择文件", "",
                                                          "*.txt, *.md, ... (*.*);;文本文档 (*.txt);;Markdown 文档 (*.md)")
            temp = True
            if filePath != "" and not isfile(filePath):
                self.msg = f"抱歉嗷~</b> 我似乎找不到文件呢 /( ̄x ̄)/~~?"
            elif filePath:
                self.currentFilePath = filePath
                with open(filePath, encoding=Tc._getFileEncoding(filePath)) as f:
                    text = f.read().replace("\u200b", "\t")
                    self.textEdit.setPlainText(text)
                self.error, fileName = 1, split(filePath)[1]

                self.msg = f"[{fileName}]</b> 已经打开!<b>o(* ̄v ̄*)o</b>" if not confmsg else f"<b>o(* ̄w ̄*)o</b> 配置已还原~!"

                self.update_file_label(fileName)
                self.updateRecent()
                # 添加入最近打开的文件菜单项
                self.addRecent(filePath)
                self.copypath.setEnabled(True)
            elif not self.currentFilePath:
                self.msg = f"咦 (⊙o⊙)?</b> 似乎没有选择打开文件嗷!"
        except Exception as e:
            if temp and filePath:
                self.error, fileName = 0, split(filePath)[1]
                self.msg = f"[{fileName}]</b> 打开失败!<b>/(ㄒoㄒ)/~~</b> 可能<b>不支持</b>此类文件嗷!"
            else:
                self.msg = f"程序异常,请到程序根目录下的 debug.log 中查看异常信息</b> "
                raise e
        if self.msg != flag:
            self.updateT.setText(self.msg)

    # 信号槽函数,更新菜单栏右侧 Label
    def _outputText(self, text):
        self.menuRLable.setText("<b>" + text)

    # 覆盖当前打开文件
    def saveCurrentFile(self):
        if isfile(self.currentFilePath) and self.error:
            path, fileName = split(self.currentFilePath)
            try:
                _isOrNo = QMessageBox.question(
                    self, '保存文件 (~^_^)~',
                    f'是否<span style="color:red; font-weight:bold;">覆盖<b>{fileName}</b>文件</span>(建议<b>备份</b>后保存)进行保存?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                if _isOrNo == QMessageBox.Yes:
                    if not self.saveConfig(self.currentFilePath):
                        with open(self.currentFilePath, "w", encoding=Tc._getFileEncoding(self.currentFilePath)) as f:
                            f.write(self.textEdit.toPlainText())
                    else:
                        Tc._loadConfig(True)
                    self.msg = f"[{fileName}]</b> 文件保存成功!<b>~(*^_^*)~</b>"
                else:
                    self.msg = f"取消保存,建议备份文件后保存</b> <b>(~^o^~)</b>"

            except Exception as e:
                self.msg = f"[{fileName}]</b> 文件保存失败!<b>~(*ㄒoㄒ*)~</b> 异常原因:<span  style='color: red; font-weight:bold;'>{str(e)}</span>"
                self._log.exception(e)
            self.updateT.setText(self.msg)
        else:
            self.saveFile()

    # 保存文件
    def saveFile(self, saveAs=0):
        flag = 0
        try:
            path, fileName = "", "FishC.txt"
            if self.currentFilePath:
                path, filePath = split(self.currentFilePath)
            filePath, _ = QFileDialog.getSaveFileName(self, "保存文件", fileName,
                                                      "文本文档 (*.txt);;Markdown 文档 (*.md)")
            if filePath:
                if saveAs or not self.saveConfig(filePath):
                    if saveAs or not exists(filePath):
                        encoding = "utf-8"
                    else:
                        encoding = Tc._getFileEncoding(filePath)
                    with open(filePath, "w", encoding= encoding)  as f:
                        f.write(self.textEdit.toPlainText())
                _, fileName = split(filePath)
                self.msg = f"[{fileName}]</b> 文件保存成功!<b>~(= ̄ω ̄=)~</b>"
                if saveAs:
                    self.currentFilePath = filePath
                self.update_file_label(fileName)
                flag = 1
                self.addRecent(filePath)
                self.copypath.setEnabled(True)
        except Exception as e:
            if filePath:
                _, fileName = split(filePath)
                self.msg = f"[{fileName}]</b> 文件保存失败!<b>~(*ㄒoㄒ*)~</b> 异常原因:<span  style='color: red; font-weight:bold;'>{str(e)}</span>"
            else:
                self.msg = f"~(*ㄒoㄒ*)~ </b> 文件保存失败!异常原因:<span  style='color: red; font-weight:bold;'>{str(e)}</span>"
            self._log.exception(e)
            flag = 1
        if flag:
            self.updateT.setText(self.msg)

    # 保存配置
    def saveConfig(self, path):
        if exists(path) and samefile(self.config_path, path):
            self.config_reader.save_Config(ConfigReader(self.textEdit.toPlainText(), True).config._sections)
            self.msg = "[config.ini}]</b> 文件保存成功!<b>~(= ̄ω ̄=)~</b>"
            self.update_file_label("config.ini")
            self.addRecent(path)
            self.copypath.setEnabled(True)
            return True
        return False

    # 转换
    def convert(self):
        try:
            if not self.textEdit.toPlainText().strip():
                raise Exception("文本框中无内容,转换个空气...给我系内!")
            elif self.currentFilePath and samefile(self.currentFilePath, self.config_path):
                raise Exception("(ˉ▽ˉ;)... 这是我配置文件,<b>无需</b>转换嗷!")
            currentIndex = self.convertComboBox.currentIndex()
            if currentIndex == 0:
                # 进行 Markdown 转 Discuz 操作
                text = Tc.markdownToDiscuz(self.textEdit.toPlainText())
                self.textEdit.setPlainText(text)
                self.msg = f"Markdown → Discuz</b> 文件转换成功!<b>(~ ̄▽ ̄)~</b>"
            elif currentIndex == 1:
                # 进行 Discuz 转 Markdown 操作
                text = Tc.discuzToMarkdown(self.textEdit.toPlainText())
                self.textEdit.setPlainText(text)
                self.msg = f"Discuz → Markdown</b> 文件转换成功!<b>~( ̄▽ ̄~)</b>"

        except Exception as e:
            self.msg = f"可恶</b>,失败了!<b>~(/T^T\)~</b>异常原因:<span style='color=red; font-weight:bold;'>{str(e)}</span>"
            self._log.exception(e)
        self.updateT.setText(self.msg)

    # 显示帮助
    def showHelp(self):
        reply = QMessageBox.question(
            self, '是否跳转?^3^',
            '是否<span style="color:red; font-weight:bold;">跳转</span>到帮助页面?',
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.updateT.setText(f"mua~</b>偷偷看帮助被我逮着啦!<b>(* ̄3 ̄)~</b>")
            url = QUrl("https://fishc.com.cn/thread-227318-1-1.html")
            QDesktopServices.openUrl(url)
        else:
            self.updateT.setText(f"(o/>^<\o)</b> 难道......你对我不感兴趣吗?")

    # 最小化窗口
    def minimizeWindow(self):
        self.showMinimized()

    # 最大化或还原窗口
    def showMaximizedOrRestore(self):
        if self.isMaximized():
            self.showNormal()
            self.updateT.setText("嗷呜!~(●'◡'●~)</b> 我变回来啦!")
            self.maxButton.setIcon(QIcon(self._Maximize))
            self.maxButton.setText("最大化")
            self.maxButton.setToolTip("最大化窗口(快捷键:Ctrl+D)")
        else:
            self.showMaximized()
            self.updateT.setText("哇嗷!(~●'◡'●)~</b> 我变大啦!")
            self.maxButton.setIcon(QIcon(self._Restore))
            self.maxButton.setText("恢复窗口")
            self.maxButton.setToolTip("恢复窗口大小(快捷键:Ctrl+D)")

    # 窗口栏点击最大化
    def resizeEvent(self, event):
        if self.isMaximized():
            self.updateT.setText("嗷呜!~(●'◡'●~)</b> 我变回来啦!")
        else:
            if not self.first:
                self.updateT.setText("哇嗷!(~●'◡'●)~</b> 我变大啦!")
        self.first = 0
        return super().resizeEvent(event)

    # 窗口栏点击关闭
    def closeEvent(self, event):
        self.config_reader.save_Config()
        reply = QMessageBox.question(self, '确认', '确定退出程序?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

        self.updateT.setText("(/≧▽≦)/</b> 我就知道你不会离开我 ❤~")

    # 显示关于
    def showAbout(self):
        # 弹出作者信息框
        aboutBox = QMessageBox(self)
        aboutBox.setWindowTitle("关于作者")
        aboutBox.setIconPixmap(QPixmap("UangSC"))
        aboutBox.setStyleSheet("background-color:#CBDCE9;")
        aboutBox.setText(
            f"<br><div style='text-align:center; font-size:12pt; font-family:STKaiti; font-weight:bold;'>版本号:{self._version}<hr>作者:Twilight6(UangSC)<br>QQ:327981933<br>码云:<a href='https://gitee.com/UangSC/TextConverter'>啥都木有</a><br>论坛:<a href='https://fishc.com.cn/space-uid-854664.html'>鱼C论坛</a><br>Email:UangSC@foxmail.com</div>")
        aboutBox.setTextFormat(Qt.RichText)
        desktop = QDesktopWidget()
        screenWidth = desktop.screenGeometry().width()
        screenHeight = desktop.screenGeometry().height()
        centerX = int(screenWidth / 2)
        centerY = int(screenHeight / 2)
        x = aboutBox.sizeHint().width()
        y = aboutBox.sizeHint().height()
        aboutBox.move(centerX - int((aboutBox.width() + x) / 2), centerY - int((aboutBox.height() + y) / 2))
        aboutBox.exec_()
        self.updateT.setText("(*/ω\*)</b> 主动就会有结果 ❤~")


ConfigReader.py

                               
登录/注册后可看大图
import configparser

class ConfigReader:
    def __init__(self, file_path, read_string=False):
        self.configPath = file_path
        self.config = configparser.RawConfigParser(comment_prefixes='/', allow_no_value=True)
        self.config.optionxform = str
        self.restore = 0
        if read_string:
            self.config.read_string(file_path)
        else:
            try:
                with open(file_path) as file:
                    self.config.read_file(file)
            except UnicodeDecodeError:
                with open(file_path, encoding="utf-8") as file:
                    self.config.read_file(file)


    def update_recentFile(self, recentList: list):
        self.config._sections["files"]["RecentFile"] = recentList

    def save_Config(self, confDict=None):
        if self.restore:
            recentList = self.config._sections["files"]["RecentFile"]
            self.config.clear()
            self._load_dict_to_dict(confDict)
            self.config._sections["files"]["RecentFile"] = recentList
        elif confDict != None:
            self.config.clear()
            self._load_dict_to_dict(confDict)
        try:
            with open(self.configPath, "w") as file:
                self.config.write(file)
        except:
            with open(self.configPath, "w", encoding="utf-8") as file:
                self.config.write(file)

    def _load_dict_to_dict(self, conf_dict):
        for section_name, section in conf_dict.items():
            self.config.add_section(section_name)
            for key, value in section.items():
                self.config.set(section_name, key, value)

    def get_img(self, img_name):
        """
        获取 [icons] 节中图片路径
        :param img_name: 配置文件中的属性名
        :return: 返回图片相对路径
        """
        return self.config.get("icons", img_name)


    def get_style(self, style_name):
        """
        获取 [styles] 节中的对应属性
        :param style_name: 配置文件中的属性名
        :return: 返回属性值
        """
        return self.config.get("styles", style_name)


    def get_fonts(self, font_name):
        """
        获取 [fonts] 节中的属性
        :param font_name: 配置文件中的属性名
        :return: 返回属性值
        """
        return self.config.get("fonts", font_name)


    def get_settings(self, set_name):
        """
        获取 [settings] 节中的属性
        :param set_name: 配置文件中的属性名
        :return: 返回属性值
        """
        return self.config.get("settings", set_name)

    def get_files(self, file_name):
        """
        获取 [files] 节中的属性
        :param file_name: 配置文件中的属性名
        :return: 返回属性值
        """
        return self.config.get("files", file_name)


main.py

                               
登录/注册后可看大图
from os import getcwd,sep
from os.path import basename
import sys

from PyQt5.QtWidgets import QApplication
from ui.TextGUI import MyTextEdit
import logging


if __name__ == '__main__':
    logger = logging.getLogger(__name__)
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s %(levelname)s %(message)s\n' + "=" * 60,
        handlers=[
            logging.FileHandler('deBug.log', mode='a'),
            logging.StreamHandler()
        ]
    )
    # 关闭 chardet 日志的输出
    logging.getLogger('chardet.charsetprober').disabled = True
    logging.getLogger('chardet.universaldetector').disabled = True
    try:
        app = QApplication(sys.argv)
        app.setStyle("Fusion")
        exePath = getcwd() + sep + basename(sys.argv[0])
        myTextEdit = MyTextEdit(exePath, logging)
        myTextEdit.show()
        sys.exit(app.exec_())
    except Exception as e:
        logger.exception(e)


帖子创作不易,评个分再走呗~


                               
登录/注册后可看大图


[/b]

评分

参与人数 2荣誉 +8 鱼币 +8 贡献 +6 收起 理由
liuhongrun2022 + 5 + 5 + 3
王逗比666 + 3 + 3 + 3

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-23 10:08:22 | 显示全部楼层
顶顶顶
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-28 13:26:41 | 显示全部楼层
seeit
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-30 22:12:00 | 显示全部楼层
突然发现告诉gpt转换的规则,gpt能做得更好,何不直接接入chatgpt的api呢,或者让chatgpt给出转换的代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-31 09:28:11 | 显示全部楼层
isdkz 发表于 2023-5-30 22:12
突然发现告诉gpt转换的规则,gpt能做得更好,何不直接接入chatgpt的api呢,或者让chatgpt给出转换的代码{:1 ...



这不见得,我并没购买接口,但这代码本就是我不断询问 GPT 后再不断修改的成果

而 3.5 接口,还是存在很多问题,无法满足需求,都需要进行修改,PyQt5 的 GUI 也是他整的

我实际上在编写这个程序时候,实际上就是给予 GPT 一个个小任务,GPT 反馈给我

然后我修复 GPT 的 Bug 和 逻辑上的问题,在将这些小任务全部组装起来而成的

另外,能直接调用 GPT 就写不出这帖子,再编写程序的时候自己也在不断的学习

直接调用接口?当然可以,但我觉得很无聊。

你一句 GPT 能做的更好,虽然是现实,但是在我这听来,反而使我有些心凉...

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-6 00:23:08 | 显示全部楼层
bug:
```python
# hello world
```
{code}
python
# hello world
{/code}
图:
动画.gif

评分

参与人数 1荣誉 +2 鱼币 +3 贡献 +3 收起 理由
Twilight6 + 2 + 3 + 3 鱼C有你更精彩^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-8 13:54:30 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-10 10:31:48 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-7-19 20:24:36 | 显示全部楼层
看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-7-19 22:24:38 | 显示全部楼层
匠心巨制,求评分:https://fishc.com.cn/thread-231007-1-1.html
马上就能申请精华了,助把力吧
渴望贡献,就差一点贡献了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-2-3 10:28:09 | 显示全部楼层
来了!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-11 14:55

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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