|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
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:免费和付费的是同样的源码都一样,希望大家能支持一下购买附件啦~
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
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> 主动就会有结果 ❤~")
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)
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]
|
评分
-
查看全部评分
|