PySide6 Win10记事本从零到一——第六章(上) 编辑菜单界面与部分功能实现
文章目录
- 第六章(上) 编辑菜单界面与部分功能实现
- 6.1 界面分析
- 6.1.1 菜单项
- 6.2 界面实现
- 6.2.1 实现思路与代码
- 6.2.2 效果演示
- 6.3 编辑操作
- 6.3.1 代码实现
- 6.3.2 功能效果演示
第六章(上) 编辑菜单界面与部分功能实现
这一部分我们会实现编辑菜单界面以及编辑操作的相关功能
6.1 界面分析

6.1.1 菜单项
在图示菜单项中包含如下元素以及对应快捷键:
- 撤销(&U)
QKeySequence.StandardKey.Undo - 剪切(&T)
KeySequence.StandardKey.Cut - 复制(&C)
QKeySequence.StandardKey.Copy - 粘贴(&P)
QKeySequence.StandardKey.Paste - 删除(&L)
QKeySequence.StandardKey.Paste - 分割线
- 使用Bing搜索
- 查找(&F)
QKeySequence.StandardKey.Find - 查找下一个(&N)
QKeySequence.StandardKey.FindNext - 查找上一个(&V)
QKeySequence.StandardKey.FindPrevious - 替换(&R)
QKeySequence.StandardKey.Replace - 转到(&G)
Ctrl+G - 分割线
- 全选(&A)
QKeySequence.StandardKey.SelectAll - 时间/日期(&D)
F5
可以看出都是一些QAction类。
6.2 界面实现
这里截取自第四章小试牛刀的代码part4/more_menu/custom_menu.py,有修改
6.2.1 实现思路与代码
实现思路:
- 自定义类 继承自
QMenu - 使用
setup_ui方法来设置界面 - 将其添加到构造函数中,初始化显示
- 主界面:实例化 该菜单
custom_menu.py 省略了 文件菜单界面的实现
from PySide6.QtWidgets import QMenu
from PySide6.QtGui import QKeySequenceclass FileMenu(QMenu):"""文件菜单:param QMenu: PySide6 菜单类"""pass class EditMenu(QMenu):"""编辑菜单:param QMenu: PySide6 菜单类"""def __init__(self):"""初始化"""super().__init__()self.setup_ui()def setup_ui(self):"""设置菜单项"""self.setTitle("编辑(&E)",)undo = self.addAction("撤销(&U)",QKeySequence.StandardKey.Undo)self.addSeparator()cut = self.addAction("剪切(&T)",QKeySequence.StandardKey.Cut)copy = self.addAction("复制(&C)",QKeySequence.StandardKey.Copy)paste = self.addAction("粘贴(&P)",QKeySequence.StandardKey.Paste)delete = self.addAction("删除(&L)",QKeySequence.StandardKey.Delete)self.addSeparator()search = self.addAction("使用Bing搜索")find_ = self.addAction("查找(&F)",QKeySequence.StandardKey.Find)find_next = self.addAction("查找下一个(&N)",QKeySequence.StandardKey.FindNext)find_previous = self.addAction("查找上一个(&V)",QKeySequence.StandardKey.FindPrevious)replace = self.addAction("替换(&R)",QKeySequence.StandardKey.Replace)goto =self.addAction("转到(&G)","Ctrl+G")self.addSeparator()select_all = self.addAction("全选(&A)",QKeySequence.StandardKey.SelectAll)date_ = self.addAction("时间/日期(&D)","F5")
注意:需要查看其他菜单项界面的实现见项目
注意:纯文本编辑器的canPaste() 虽然也可以检测剪贴板,但只有实例化的时候检测一次!而且只有windows和
LinuxX11桌面平台支持,所以不推荐使用
注意: 为了方便管理自定义菜单类,后续都会添加自定义菜单类到
custom_menu.py。并且为了减少代码量,我们在添加行为(QAction)都会直接添加快捷键(ShortCut)
notepad_main.py
from PySide6.QtWidgets import QMainWindow,QPlainTextEdit,QFrame,QMenu,QApplication
from custom_menu import FileMenu,EditMenu
import sysclass NotepadMain(QMainWindow):"""记事本主界面:param QMainWindow: 主窗口"""def __init__(self):"""初始化"""super().__init__()self.setup_ui()def setup_ui(self):"""设置用户界面"""# 设置窗口标题self.setWindowTitle("无标题 - 记事本")# 设置窗口大小self.resize(800, 500)# 创建菜单栏 设置为私有属性self.__menubar = self.menuBar()# 实例化自定义菜单file_menu = FileMenu()edit_menu = EditMenu()# 添加其他自定义的菜单self.__menubar.addMenu(file_menu)self.__menubar.addMenu(edit_menu)# 示例化纯文本编辑 plain_text_edit = QPlainTextEdit()# 消除框线plain_text_edit.setFrameShape(QFrame.Shape.NoFrame)# 添加纯文本编辑到 中心窗口self.setCentralWidget(plain_text_edit)# 添加状态栏目self.statusBar()def add_menu(self,menu: QMenu):"""添加按钮:param menu: PySide6 按钮类"""# 对外提供的接口self.__menubar.addMenu(menu)if __name__ == "__main__":app = QApplication(sys.argv)# 添加其他菜单notepad_main = NotepadMain()notepad_main.show()sys.exit(app.exec())
注意:
notepad_main.py相比part4/batter/notepad_main.py只是导入并添加了编辑菜单
注意:信号与槽函数连接见5.2.1 信号、槽函数以及二者的连接
6.2.2 效果演示
fedora 42 gnome 48 wayland

Kubuntu24.04_KDE5.27.12_x11

Win10工作站版

6.3 编辑操作
由于篇幅有限,这里就只实现编辑操作相关:
- 撤销(&U) 取消上一次写入的内容
- 剪切(&T) 删除所选内容并复制到剪贴板
- 复制(&C) 将所选内容直接复制到剪贴板中
- 粘贴(&P) 获取剪贴板内容并插入当前位置
- 删除(&L) 删除所选文本
- 全选(&A) 选中所有文本
重点:
- 将行为触发的信号与纯文本编辑的对应方法连接;
- 设置粘贴行为的状态,需要借助
QGuiApplication.clipboard()返回的剪切板,然后通过剪贴板是否有无文本来设置 粘贴行为是否可用
注意: 因为剪切、复制、删除都是选中后才可用,所以都借用了QPlainTextEdit.copyAvailable信号!
6.3.1 代码实现
custom_menu.py
from PySide6.QtWidgets import QMenu,QPlainTextEdit
from PySide6.QtGui import QKeySequence
from PySide6.QtGui import QGuiApplication
# 添加 文件、编辑、格式、查看、帮助 菜单class FileMenu(QMenu):"""文件菜单:param QMenu: PySide6 菜单类"""def __init__(self):"""初始化"""super().__init__()self.setup_ui()def setup_ui(self):"""设置菜单项"""self.setTitle("文件(&F)")new_file = self.addAction("新建(&N)",QKeySequence.StandardKey.New)new_window = self.addAction("新窗口(&W)","Ctrl+Shift+N")open_file = self.addAction("打开(&O)",QKeySequence.StandardKey.Open)save_file = self.addAction("保存(&S)",QKeySequence.StandardKey.Save)self.addSeparator()save_file_as = self.addAction("另存为(&A)",QKeySequence.StandardKey.SaveAs)print_file = self.addAction("打印(&P",QKeySequence.StandardKey.Print)self.addSeparator()exit_ = self.addAction("退出(&X)",QKeySequence.StandardKey.Close)class EditMenu(QMenu):"""编辑菜单:param QMenu: PySide6 菜单类"""def __init__(self):"""初始化"""super().__init__()self.setup_ui()def setup_ui(self):"""设置菜单项"""self.setTitle("编辑(&E)",)self.undo = self.addAction("撤销(&U)",QKeySequence.StandardKey.Undo)self.addSeparator()self.cut = self.addAction("剪切(&T)",QKeySequence.StandardKey.Cut)self.copy = self.addAction("复制(&C)",QKeySequence.StandardKey.Copy)self.paste = self.addAction("粘贴(&P)",QKeySequence.StandardKey.Paste)self.delete = self.addAction("删除(&L)",QKeySequence.StandardKey.Delete)self.addSeparator()search = self.addAction("使用Bing搜索")find_ = self.addAction("查找(&F)",QKeySequence.StandardKey.Find)find_next = self.addAction("查找下一个(&N)",QKeySequence.StandardKey.FindNext)find_previous = self.addAction("查找上一个(&V)",QKeySequence.StandardKey.FindPrevious)replace = self.addAction("替换(&R)",QKeySequence.StandardKey.Replace)goto =self.addAction("转到(&G)","Ctrl+G")self.addSeparator()self.select_all = self.addAction("全选(&A)",QKeySequence.StandardKey.SelectAll)date_ = self.addAction("时间/日期(&D)","F5")# 默认状态 撤销、剪切、复制、粘贴、删除 不可用self.undo.setEnabled(False)self.cut.setEnabled(False)self.copy.setEnabled(False)self.paste.setEnabled(False)self.delete.setEnabled(False)# 初始化剪贴板self.clipbaord_ = QGuiApplication.clipboard()def set_event_bind(self,plain_text_edit:QPlainTextEdit):"""设置事件绑定"""# 设置状态 撤销、剪切、复制、粘贴、删除plain_text_edit.undoAvailable.connect(self.undo.setEnabled)plain_text_edit.copyAvailable.connect(self.cut.setEnabled)plain_text_edit.copyAvailable.connect(self.copy.setEnabled)self.clipbaord_.dataChanged.connect(self.reset_paste_state)plain_text_edit.copyAvailable.connect(self.delete.setEnabled)# 撤销、剪切、复制、粘贴、删除 、全选 行为的绑定self.undo.triggered.connect(plain_text_edit.undo)self.cut.triggered.connect(plain_text_edit.cut)self.copy.triggered.connect(plain_text_edit.copy)self.paste.triggered.connect(plain_text_edit.paste)self.delete.triggered.connect(plain_text_edit.clear)self.select_all.triggered.connect(plain_text_edit.selectAll)def reset_paste_state(self):"""重新设置粘贴状态"""clipbaord_text = self.clipbaord_.text()if clipbaord_text:self.paste.setEnabled(True)else:self.paste.setEnabled(False)
常见问题:为什么设置撤销、剪切、复制、粘贴、删除行为状态,不需要借助匿名函数或者自定义槽函数?因为
QPlainTextEdit.copyAvailable与QPlainTextEdit.undoAvailable信号会传递布尔值正好与设置是否可用(setEnabled)对应,所以不需要借助匿名函数或者自定义槽函数。详情见官网描述:QPlainTextEdit.copyAvailable、QPlainTextEdit.undoAvailable
notepad_main.py
from PySide6.QtWidgets import QMainWindow,QPlainTextEdit,QFrame,QMenu,QApplication
from custom_menu import FileMenu,EditMenu
import sysclass NotepadMain(QMainWindow):"""记事本主界面:param QMainWindow: 主窗口"""def __init__(self):"""初始化"""super().__init__()self.setup_ui()self.set_event_bind()def setup_ui(self):"""设置用户界面"""# 设置窗口标题self.setWindowTitle("记事本主界面")# 设置窗口大小self.resize(800, 500)# 创建菜单栏 设置为私有属性self.__menubar = self.menuBar()# 实例化自定义菜单file_menu = FileMenu()self.edit_menu = EditMenu()# 添加其他自定义的菜单self.__menubar.addMenu(file_menu)self.__menubar.addMenu(self.edit_menu)# 示例化纯文本编辑 self.plain_text_edit = QPlainTextEdit()# 消除框线self.plain_text_edit.setFrameShape(QFrame.Shape.NoFrame)# 添加纯文本编辑到 中心窗口self.setCentralWidget(self.plain_text_edit)# 添加状态栏目self.statusBar()def set_event_bind(self):"""设置事件绑定"""self.edit_menu.set_event_bind(self.plain_text_edit)def add_menu(self,menu: QMenu):"""添加按钮:param menu: PySide6 按钮类"""# 对外提供的接口self.__menubar.addMenu(menu)if __name__ == "__main__":app = QApplication(sys.argv)# 添加其他菜单notepad_main = NotepadMain()notepad_main.show()sys.exit(app.exec())
6.3.2 功能效果演示
PySide6 Win10记事本从零到一——第六章(上)功能效果演示
