Python結(jié)合PyQt5實現(xiàn)MD(Markdown)轉(zhuǎn)DOCX工具
下面是一個使用 Python 和 PyQt5 實現(xiàn)的 Markdown 轉(zhuǎn) DOCX 工具,具有美觀的圖形界面,支持表格和代碼塊轉(zhuǎn)換,并提供轉(zhuǎn)換預(yù)覽功能。
效果圖

功能特點
一比一復刻 Markdown 格式到 DOCX
支持表格、代碼塊等復雜元素轉(zhuǎn)換
美觀的圖形界面
轉(zhuǎn)換過程實時預(yù)覽
文件選擇對話框
代碼實現(xiàn)
import sys
import os
from markdown import markdown
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.oxml.ns import qn
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
QPushButton, QFileDialog, QTextEdit, QLabel,
QWidget, QMessageBox, QProgressBar)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QIcon
class MarkdownToDocxConverter(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.setWindowIcon(QIcon('icon.png')) # 請準備一個圖標文件或刪除這行
def initUI(self):
self.setWindowTitle('Markdown 轉(zhuǎn) DOCX 工具')
self.setGeometry(300, 300, 800, 600)
# 主窗口部件
main_widget = QWidget()
self.setCentralWidget(main_widget)
# 主布局
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
# 標題
title_label = QLabel('Markdown 轉(zhuǎn) DOCX 轉(zhuǎn)換器')
title_label.setFont(QFont('Microsoft YaHei', 16, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet('color: #2c3e50; margin-bottom: 20px;')
main_layout.addWidget(title_label)
# 文件選擇區(qū)域
file_layout = QHBoxLayout()
self.md_file_label = QLabel('未選擇文件')
self.md_file_label.setFont(QFont('Microsoft YaHei', 10))
self.md_file_label.setStyleSheet('border: 1px solid #ddd; padding: 5px;')
self.md_file_label.setFixedHeight(30)
select_btn = QPushButton('選擇 Markdown 文件')
select_btn.setFont(QFont('Microsoft YaHei', 10))
select_btn.setStyleSheet('''
QPushButton {
background-color: #3498db;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #2980b9;
}
''')
select_btn.clicked.connect(self.select_md_file)
file_layout.addWidget(self.md_file_label, stretch=4)
file_layout.addWidget(select_btn, stretch=1)
main_layout.addLayout(file_layout)
# 預(yù)覽區(qū)域
preview_label = QLabel('預(yù)覽內(nèi)容:')
preview_label.setFont(QFont('Microsoft YaHei', 10, QFont.Bold))
main_layout.addWidget(preview_label)
self.preview_text = QTextEdit()
self.preview_text.setFont(QFont('Consolas', 10))
self.preview_text.setReadOnly(True)
self.preview_text.setStyleSheet('''
QTextEdit {
border: 1px solid #ddd;
padding: 10px;
background-color: #f9f9f9;
}
''')
main_layout.addWidget(self.preview_text, stretch=3)
# 進度條
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.progress_bar.setTextVisible(True)
self.progress_bar.setStyleSheet('''
QProgressBar {
border: 1px solid #ddd;
border-radius: 3px;
text-align: center;
height: 20px;
}
QProgressBar::chunk {
background-color: #2ecc71;
width: 10px;
}
''')
main_layout.addWidget(self.progress_bar)
# 轉(zhuǎn)換按鈕
convert_btn = QPushButton('轉(zhuǎn)換為 DOCX')
convert_btn.setFont(QFont('Microsoft YaHei', 12, QFont.Bold))
convert_btn.setStyleSheet('''
QPushButton {
background-color: #2ecc71;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
margin-top: 15px;
}
QPushButton:hover {
background-color: #27ae60;
}
QPushButton:disabled {
background-color: #95a5a6;
}
''')
convert_btn.clicked.connect(self.convert_to_docx)
convert_btn.setEnabled(False)
self.convert_btn = convert_btn
main_layout.addWidget(convert_btn, alignment=Qt.AlignCenter)
# 狀態(tài)欄
self.statusBar().showMessage('準備就緒')
# 成員變量
self.md_file_path = ''
def select_md_file(self):
options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName(
self, "選擇 Markdown 文件", "",
"Markdown Files (*.md *.markdown);;All Files (*)",
options=options
)
if file_path:
self.md_file_path = file_path
self.md_file_label.setText(file_path)
self.convert_btn.setEnabled(True)
# 預(yù)覽文件內(nèi)容
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
self.preview_text.setPlainText(content)
self.statusBar().showMessage('文件加載成功')
except Exception as e:
QMessageBox.warning(self, '錯誤', f'無法讀取文件: {str(e)}')
self.statusBar().showMessage('文件讀取失敗')
def convert_to_docx(self):
if not self.md_file_path:
QMessageBox.warning(self, '警告', '請先選擇 Markdown 文件')
return
# 設(shè)置保存路徑
options = QFileDialog.Options()
save_path, _ = QFileDialog.getSaveFileName(
self, "保存 DOCX 文件",
os.path.splitext(self.md_file_path)[0] + '.docx',
"Word Documents (*.docx);;All Files (*)",
options=options
)
if not save_path:
return
self.progress_bar.setValue(10)
self.statusBar().showMessage('正在轉(zhuǎn)換...')
QApplication.processEvents() # 更新UI
try:
# 讀取Markdown內(nèi)容
with open(self.md_file_path, 'r', encoding='utf-8') as f:
md_content = f.read()
self.progress_bar.setValue(30)
# 創(chuàng)建Word文檔
doc = Document()
# 設(shè)置默認字體
doc.styles['Normal'].font.name = '微軟雅黑'
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
doc.styles['Normal'].font.size = Pt(10.5)
# 轉(zhuǎn)換Markdown為HTML
html_content = markdown(md_content, extensions=[
'extra', # 支持表格、代碼塊等
'codehilite', # 代碼高亮
'tables', # 表格支持
'fenced_code' # 圍欄代碼塊
])
self.progress_bar.setValue(50)
# 將HTML內(nèi)容添加到Word文檔
self.add_html_to_doc(html_content, doc)
self.progress_bar.setValue(80)
# 保存文檔
doc.save(save_path)
self.progress_bar.setValue(100)
self.statusBar().showMessage('轉(zhuǎn)換完成!')
QMessageBox.information(self, '成功', '文件轉(zhuǎn)換完成!')
except Exception as e:
QMessageBox.critical(self, '錯誤', f'轉(zhuǎn)換過程中出錯: {str(e)}')
self.statusBar().showMessage('轉(zhuǎn)換失敗')
finally:
self.progress_bar.setValue(0)
def add_html_to_doc(self, html, doc):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
for element in soup.children:
if element.name == 'h1':
self.add_heading(doc, element.text, 0)
elif element.name == 'h2':
self.add_heading(doc, element.text, 1)
elif element.name == 'h3':
self.add_heading(doc, element.text, 2)
elif element.name == 'h4':
self.add_heading(doc, element.text, 3)
elif element.name == 'h5':
self.add_heading(doc, element.text, 4)
elif element.name == 'h6':
self.add_heading(doc, element.text, 5)
elif element.name == 'p':
self.add_paragraph(doc, element.text)
elif element.name == 'ul':
self.add_list(doc, element, False)
elif element.name == 'ol':
self.add_list(doc, element, True)
elif element.name == 'table':
self.add_table(doc, element)
elif element.name == 'pre':
self.add_code_block(doc, element)
elif element.name == 'blockquote':
self.add_quote(doc, element)
elif element.name == 'hr':
self.add_horizontal_rule(doc)
def add_heading(self, doc, text, level):
heading = doc.add_heading(text, level)
# 設(shè)置中文字體
for run in heading.runs:
run.font.name = '微軟雅黑'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
def add_paragraph(self, doc, text):
p = doc.add_paragraph(text)
# 設(shè)置中文字體
for run in p.runs:
run.font.name = '微軟雅黑'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
def add_list(self, doc, element, ordered):
for li in element.find_all('li', recursive=False):
if ordered:
doc.add_paragraph(li.text, style='List Number')
else:
doc.add_paragraph(li.text, style='List Bullet')
# 遞歸處理子列表
for child in li.children:
if child.name in ['ul', 'ol']:
self.add_list(doc, child, child.name == 'ol')
def add_table(self, doc, element):
rows = element.find_all('tr')
if not rows:
return
# 創(chuàng)建表格
table = doc.add_table(rows=len(rows), cols=len(rows[0].find_all(['th', 'td'])))
table.style = 'Table Grid' # 添加邊框
for i, row in enumerate(rows):
cells = row.find_all(['th', 'td'])
for j, cell in enumerate(cells):
table.cell(i, j).text = cell.get_text()
# 設(shè)置中文字體
for paragraph in table.cell(i, j).paragraphs:
for run in paragraph.runs:
run.font.name = '微軟雅黑'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
# 表頭加粗
if cell.name == 'th':
for paragraph in table.cell(i, j).paragraphs:
for run in paragraph.runs:
run.font.bold = True
def add_code_block(self, doc, element):
code = element.find('code')
if not code:
return
code_text = code.get_text()
# 添加代碼段落
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.5)
p.paragraph_format.space_before = Pt(6)
p.paragraph_format.space_after = Pt(6)
run = p.add_run(code_text)
run.font.name = 'Consolas'
run.font.size = Pt(10)
run.font.color.rgb = RGBColor(0x36, 0x36, 0x36)
# 添加灰色背景
shading_elm = p._element.get_or_add_pPr().get_or_add_shd()
shading_elm.set(qn('w:fill'), 'F0F0F0')
def add_quote(self, doc, element):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.5)
p.paragraph_format.first_line_indent = Inches(-0.25)
p.paragraph_format.space_before = Pt(6)
p.paragraph_format.space_after = Pt(6)
run = p.add_run(element.get_text())
run.font.name = '微軟雅黑'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
run.font.italic = True
run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
# 添加左邊框
p._element.get_or_add_pPr().get_or_add_pBdr().left.val = 'single'
p._element.get_or_add_pPr().get_or_add_pBdr().left.sz = 4
p._element.get_or_add_pPr().get_or_add_pBdr().left.color = 'auto'
def add_horizontal_rule(self, doc):
p = doc.add_paragraph()
p.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
run = p.add_run('―' * 30) # 使用長破折號作為分隔線
run.font.color.rgb = RGBColor(0xCC, 0xCC, 0xCC)
if __name__ == '__main__':
app = QApplication(sys.argv)
# 設(shè)置應(yīng)用程序字體
font = QFont('Microsoft YaHei', 10)
app.setFont(font)
# 設(shè)置樣式表
app.setStyleSheet('''
QMainWindow {
background-color: #f5f7fa;
}
QLabel {
color: #34495e;
}
''')
converter = MarkdownToDocxConverter()
converter.show()
sys.exit(app.exec_())
使用說明
運行程序后,點擊"選擇 Markdown 文件"按鈕選擇要轉(zhuǎn)換的.md文件
文件內(nèi)容將顯示在預(yù)覽框中
點擊"轉(zhuǎn)換為 DOCX"按鈕選擇保存位置并開始轉(zhuǎn)換
轉(zhuǎn)換過程中會顯示進度條和狀態(tài)信息
轉(zhuǎn)換完成后會彈出提示框
依賴安裝
在運行此程序前,需要安裝以下依賴:
pip install PyQt5 markdown python-docx beautifulsoup4
功能擴展建議
- 可以添加批量轉(zhuǎn)換功能
- 可以增加對更多Markdown擴展語法的支持
- 可以添加主題切換功能
- 可以增加轉(zhuǎn)換歷史記錄功能
這個工具提供了美觀的界面和完整的Markdown到DOCX轉(zhuǎn)換功能,支持表格、代碼塊等復雜元素的轉(zhuǎn)換,并提供了實時預(yù)覽和進度顯示。
到此這篇關(guān)于Python結(jié)合PyQt5實現(xiàn)MD(Markdown)轉(zhuǎn)DOCX工具的文章就介紹到這了,更多相關(guān)Python Markdown轉(zhuǎn)DOCX內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用Python打造專業(yè)演示文稿轉(zhuǎn)換器(Markdown轉(zhuǎn)PPT)
- 使用Python實現(xiàn)Markdown轉(zhuǎn)Word工具
- 使用Python開發(fā)Markdown兼容公式格式轉(zhuǎn)換工具
- 使用Python將Markdown文件轉(zhuǎn)換為Word的三種方法
- 基于Python實現(xiàn)Markdown轉(zhuǎn)ePub
- Python實現(xiàn)快速提取Word表格并轉(zhuǎn)Markdown
- 利用Python實現(xiàn)Markdown文檔格式轉(zhuǎn)換詳解
- 使用Python構(gòu)建Markdown轉(zhuǎn)Word文檔轉(zhuǎn)換器
- 使用Python轉(zhuǎn)換Markdown文件為Word文檔
相關(guān)文章
python獲取當前時間對應(yīng)unix時間戳的方法
這篇文章主要介紹了python獲取當前時間對應(yīng)unix時間戳的方法,涉及Python時間操作的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下2015-05-05
python實現(xiàn)數(shù)據(jù)清洗(缺失值與異常值處理)
今天小編就為大家分享一篇python實現(xiàn)數(shù)據(jù)清洗(缺失值與異常值處理),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12
python如何實現(xiàn)excel數(shù)據(jù)添加到mongodb
本文介紹了python是如何實現(xiàn)excel數(shù)據(jù)添加到mongodb,為了將數(shù)據(jù)導入mongodb,引入了pymongo,xlrd包,需要的朋友可以參考下2015-07-07
Python之根據(jù)輸入?yún)?shù)計算結(jié)果案例講解
這篇文章主要介紹了Python之根據(jù)輸入?yún)?shù)計算結(jié)果案例講解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
Python+wxPython打造智能網(wǎng)頁截圖工具
在網(wǎng)站測試、UI審查或文檔編寫過程中,我們常常需要對網(wǎng)站的所有頁面進行截圖記錄,下面我們就來看看如何使用Python開發(fā)一個自動化工具,實現(xiàn)一鍵遍歷網(wǎng)站所有鏈接并生成帶截圖的Excel報告吧2025-10-10

