from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.Qt import * import os import sys FONT_SIZES = [7, 8, 9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288] HTML_EXTENSIONS = [".htm", ".html"] # 文件路径分割成文件名和扩展名两部分 def splitext(p): return os.path.splitext(p)[1].lower() class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = QTextEdit() self.setWindowIcon(QIcon("D:/python_study/python课程设计/Typora/icon/img.png")) self.editor.setAutoFormatting(QTextEdit.AutoAll) self.editor.selectionChanged.connect(self.update_format) font = QFont("Times", 12) self.editor.setFont(font) self.editor.setFontPointSize(12) self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) 文件工具栏 = QToolBar("文件") 文件工具栏.setIconSize(QSize(20, 20)) self.addToolBar(文件工具栏) 文件菜单 = self.menuBar().addMenu("&文件") 打开_action = QAction( QIcon("D:/python_study/python课程设计/Typora/icon/打开文件.png"), "打开文件", self, ) 打开_action.triggered.connect(self.file_open) 文件菜单.addAction(打开_action) 文件工具栏.addAction(打开_action) 保存_action = QAction( QIcon(os.path.join("Typora/icon", "保存文件.png")), "保存", self ) 保存_action.triggered.connect(self.file_save) 文件菜单.addAction(保存_action) 文件工具栏.addAction(保存_action) 另存为_action = QAction( QIcon(os.path.join("Typora/icon", "另存为.png")), "另存为", self ) 另存为_action.triggered.connect(self.file_saveas) 文件菜单.addAction(另存为_action) 文件工具栏.addAction(另存为_action) 编辑工具栏 = QToolBar("编辑") 编辑工具栏.setIconSize(QSize(20, 20)) self.addToolBar(编辑工具栏) 编辑菜单 = self.menuBar().addMenu("&编辑") 撤回_action = QAction( QIcon(os.path.join("Typora/icon", "撤回.png")), "撤回", self ) 撤回_action.triggered.connect(self.editor.undo) 编辑菜单.addAction(撤回_action) 重做_action = QAction( QIcon(os.path.join("Typora/icon", "重做.png")), "重做", self ) 重做_action.triggered.connect(self.editor.redo) 编辑工具栏.addAction(重做_action) 编辑菜单.addAction(重做_action) 编辑菜单.addSeparator() 剪切_action = QAction( QIcon(os.path.join("Typora/icon", "剪切.png")), "剪切", self ) 剪切_action.setShortcut(QKeySequence.Cut) 剪切_action.triggered.connect(self.editor.cut) 编辑工具栏.addAction(剪切_action) 编辑菜单.addAction(剪切_action) 复制_action = QAction( QIcon(os.path.join("Typora/icon", "复制.png")), "复制", self ) 剪切_action.setShortcut(QKeySequence.Copy) 复制_action.triggered.connect(self.editor.copy) 编辑工具栏.addAction(复制_action) 编辑菜单.addAction(复制_action) 粘贴_action = QAction( QIcon(os.path.join("Typora/icon", "粘贴.png")), "粘帖", self, ) 剪切_action.setShortcut(QKeySequence.Paste) 粘贴_action.triggered.connect(self.editor.paste) 编辑工具栏.addAction(粘贴_action) 编辑菜单.addAction(粘贴_action) 全选_action = QAction( QIcon(os.path.join("Typora/icon", "selection-input.png")), "全选", self ) 剪切_action.setShortcut(QKeySequence.SelectAll) 全选_action.triggered.connect(self.editor.selectAll) 编辑菜单.addAction(全选_action) 编辑菜单.addSeparator() 换行 = QAction( QIcon(os.path.join("Typora/icon", "arrow-continue.png")), "自动换行", self ) 换行.setCheckable(True) 换行.setChecked(True) 换行.triggered.connect(self.edit_toggle_wrap) 编辑菜单.addAction(换行) 格式工具栏 = QToolBar("格式") 格式工具栏.setIconSize(QSize(20, 20)) self.addToolBar(格式工具栏) 格式菜单 = self.menuBar().addMenu("&格式") self.字体 = QFontComboBox() self.字体.currentFontChanged.connect(self.editor.setCurrentFont) 格式工具栏.addWidget(self.字体) self.字体大小 = QComboBox() self.字体大小.addItems([str(s) for s in FONT_SIZES]) self.字体大小.currentIndexChanged[str].connect( lambda s: self.editor.setFontPointSize(float(s)) ) 格式工具栏.addWidget(self.字体大小) self.加粗 = QAction( QIcon(os.path.join("Typora/icon", "字体加粗.png")), "加粗", self ) self.加粗.setShortcut(QKeySequence.Bold) self.加粗.setCheckable(True) self.加粗.toggled.connect( lambda x: self.editor.setFontWeight(QFont.Bold if x else QFont.Normal) ) 格式工具栏.addAction(self.加粗) 格式菜单.addAction(self.加粗) self.斜体 = QAction( QIcon(os.path.join("Typora/icon", "字体斜体.png")), "斜体", self ) self.斜体.setShortcut(QKeySequence.Italic) self.斜体.setCheckable(True) self.斜体.toggled.connect(self.editor.setFontItalic) 格式工具栏.addAction(self.斜体) 格式菜单.addAction(self.斜体) self.下划线 = QAction( QIcon(os.path.join("Typora/icon", "字体下划线.png")), "下划线", self ) self.下划线.setShortcut(QKeySequence.Underline) self.下划线.setCheckable(True) self.下划线.toggled.connect(self.editor.setFontUnderline) 格式工具栏.addAction(self.下划线) 格式菜单.addAction(self.下划线) 格式菜单.addSeparator() self.左对齐_action = QAction( QIcon(os.path.join("Typora/icon", "左对齐.png")), "靠左对齐", self ) self.左对齐_action.setCheckable(True) self.左对齐_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignLeft) ) 格式工具栏.addAction(self.左对齐_action) 格式菜单.addAction(self.左对齐_action) self.居中_action = QAction( QIcon(os.path.join("Typora/icon", "居中对齐.png")), "居中对齐", self ) self.居中_action.setCheckable(True) self.居中_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignCenter) ) 格式工具栏.addAction(self.居中_action) 格式菜单.addAction(self.居中_action) self.右对齐_action = QAction( QIcon(os.path.join("Typora/icon", "右对齐.png")), "靠右对齐", self ) self.右对齐_action.setCheckable(True) self.右对齐_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignRight) ) 格式工具栏.addAction(self.右对齐_action) 格式菜单.addAction(self.右对齐_action) self.左右对齐_action = QAction( QIcon(os.path.join("Typora/icon", "左右对齐.png")), "左右对齐", self ) self.左右对齐_action.setCheckable(True) self.左右对齐_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignJustify) ) 格式工具栏.addAction(self.左右对齐_action) 格式菜单.addAction(self.左右对齐_action) # 单选框 对齐_group = QActionGroup(self) 对齐_group.setExclusive(True) 对齐_group.addAction(self.左对齐_action) 对齐_group.addAction(self.居中_action) 对齐_group.addAction(self.右对齐_action) 对齐_group.addAction(self.左右对齐_action) 格式菜单.addSeparator() self._format_actions = [ self.字体, self.字体大小, self.加粗, self.斜体, self.下划线, ] self.update_format() self.update_title() self.show() def block_signals(self, objects, b): for o in objects: o.blockSignals(b) def update_format(self): # 设置信号阻塞 self.block_signals(self._format_actions, True) self.字体.setCurrentFont(self.editor.currentFont()) self.字体大小.setCurrentText(str(int(self.editor.fontPointSize()))) self.斜体.setChecked(self.editor.fontItalic()) self.下划线.setChecked(self.editor.fontUnderline()) self.加粗.setChecked(self.editor.fontWeight() == QFont.Bold) self.左对齐_action.setChecked(self.editor.alignment() == Qt.AlignLeft) self.居中_action.setChecked(self.editor.alignment() == Qt.AlignCenter) self.右对齐_action.setChecked(self.editor.alignment() == Qt.AlignRight) self.左右对齐_action.setChecked(self.editor.alignment() == Qt.AlignJustify) self.block_signals(self._format_actions, False) def dialog_critical(self, s): dlg = QMessageBox(self) dlg.setText(s) dlg.setIcon(QMessageBox.Critical) dlg.show() def file_open(self): path, _ = QFileDialog.getOpenFileName( self, "打开文件", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)", ) try: with open(path, "r+") as f: text = f.read() except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.editor.setText(text) self.update_title() def file_save(self): if self.path is None: return self.file_saveas() text = ( self.editor.toHtml() if splitext(self.path) in HTML_EXTENSIONS else self.editor.toPlainText() ) try: with open(self.path, "w") as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) def file_saveas(self): path, _ = QFileDialog.getSaveFileName( self, "保存文件", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)", ) if not path: return text = ( self.editor.toHtml() if splitext(path) in HTML_EXTENSIONS else self.editor.toPlainText() # 获取文本 ) try: with open(path, "w") as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.update_title() def update_title(self): self.setWindowTitle( "%s - 文本编辑器" % (os.path.basename(self.path) if self.path else "Untitled") ) def edit_toggle_wrap(self): self.editor.setLineWrapMode(1 if self.editor.lineWrapMode() == 0 else 0) if __name__ == "__main__": app = QApplication(sys.argv) app.setApplicationName("文本编辑器") window = MainWindow() window.resize(1300, 750) app.exec_()