day09——QMenu 与右键菜单实战!
🌞 欢迎来到 Day09:QMenu 与右键菜单实战!
今天我们将学习如何在 PyQt6 中创建菜单栏、右键菜单(上下文菜单),并让它与表格或控件交互。
这是你从「界面搭建」迈向「交互逻辑」的关键一步。
🧠 今日目标
掌握以下三个核心概念:
-
QMenu(菜单):可添加菜单项(Action),并绑定事件。
-
QAction(动作):菜单项的基本单元,可设置文字、图标、快捷键。
-
右键菜单(Context Menu):用户在控件上点击右键时弹出的菜单。
一、基础示例:创建菜单栏
我们先从简单的菜单栏开始:
# QMenu 与右键菜单实战
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QMenu, QMessageBox)
from PyQt6.QtGui import QAction
class MenuExample(QMainWindow):
"""
主窗口类,包含一个右键菜单
"""
def __init__(self):
super().__init__()
self.setWindowTitle("day09-菜单示例")
self.setGeometry(300, 200, 400, 300)
self.init_menu()
def init_menu(self):
"""初始化右键菜单"""
menubar = self.menuBar()
# 创建文件菜单
file_menu = menubar.addMenu("文件(&F)")
# 添加动作项
open_action = QAction("打开(&O)", self)
save_action = QAction("保存(&S)", self)
exit_action = QAction("退出(&X)", self)
# 为菜单绑定事件
open_action.triggered.connect(lambda: self.show_message("打开文件"))
save_action.triggered.connect(lambda: self.show_message("保存文件"))
exit_action.triggered.connect(self.close)
# 添加到菜单
file_menu.addAction(open_action)
file_menu.addAction(save_action)
file_menu.addSeparator()
file_menu.addAction(exit_action)
# 帮助菜单
help_menu = menubar.addMenu("帮助(&H)")
about_action = QAction("关于(&A)", self)
help_menu.addAction(about_action)
about_action.triggered.connect(lambda: self.show_message("这是一个菜单示例"))
def show_message(self, message=""):
"""显示消息框"""
QMessageBox.information(self, "提示", message)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MenuExample()
window.show()
sys.exit(app.exec())
效果如下:


-
菜单栏包含「文件」和「帮助」;
-
菜单项可触发弹窗;
-
&用于设置快捷键,如 Alt+F 打开“文件”菜单。
⚙️ 二、右键菜单(Context Menu)
菜单栏是顶部全局的,而右键菜单是局部的交互方式。
我们可以给任意控件(比如表格、文本框)添加右键菜单。
下面以文本编辑框为例👇
# 右键菜单示例
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QMenu, QMessageBox, QTextEdit)
from PyQt6.QtGui import QAction
from PyQt6.QtCore import Qt
class ContextMenuExample(QMainWindow):
"""
主窗口类,包含一个右键菜单
"""
def __init__(self):
super().__init__()
self.setWindowTitle("day09-右键菜单示例")
self.setGeometry(300, 200, 400, 300)
self.text_edit = QTextEdit(self)
self.setCentralWidget(self.text_edit)
self.text_edit.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.text_edit.customContextMenuRequested.connect(self.show_context_menu)
def show_context_menu(self, pos):
"""显示右键菜单"""
context_menu = QMenu(self)
# 创建动作
copy_action = QAction("复制", self)
paste_action = QAction("粘贴", self)
clear_action = QAction("清除", self)
# 添加菜单项
context_menu.addAction(copy_action)
context_menu.addAction(paste_action)
context_menu.addAction(clear_action)
# 绑定行为
copy_action.triggered.connect(self.text_edit.copy)
paste_action.triggered.connect(self.text_edit.paste)
clear_action.triggered.connect(self.text_edit.clear)
# 显示菜单,在光标位置
context_menu.exec(self.text_edit.mapToGlobal(pos))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ContextMenuExample()
window.show()
sys.exit(app.exec())
效果如下:

🎯 效果:
-
鼠标右击文本框;
-
弹出“复制 / 粘贴 / 清空”菜单;
-
可直接操作文本框内容。
🧩 三、进阶:右键操作 QTableWidget(实战)
我们回到前几天做的学生成绩表项目,
今天我们让用户在表格中右键点击,可以:
-
删除当前行;
-
编辑选中行;
-
查看详细信息。
# 学生成绩管理系统
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QLabel, QTableWidgetItem, QAbstractItemView,
QLineEdit, QTextEdit, QMessageBox, QMenu, QGridLayout,QHBoxLayout,QTableWidget)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QAction
class StudentTable(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("学生成绩管理系统")
self.setGeometry(300, 200, 650, 400)
self.initUI()
def initUI(self):
layout = QVBoxLayout(self)
# 表格部分
self.table = QTableWidget(0, 3)
self.table.setHorizontalHeaderLabels(["姓名","年龄","成绩"])
self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.table.setAlternatingRowColors(True)
layout.addWidget(self.table)
# 设置右键菜单策略
self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.table.customContextMenuRequested.connect(self.show_context_menu)
# 输入行,按钮部分
input_layout = QHBoxLayout()
self.name_input = QLineEdit()
self.age_input = QLineEdit()
self.score_input = QLineEdit()
input_layout.addWidget(QLabel("姓名"))
input_layout.addWidget(self.name_input)
input_layout.addWidget(QLabel("年龄"))
input_layout.addWidget(self.age_input)
input_layout.addWidget(QLabel("成绩"))
input_layout.addWidget(self.score_input)
layout.addLayout(input_layout)
# 按钮部分
button_layout = QHBoxLayout()
add_button = QPushButton("添加")
add_button.clicked.connect(self.add_student)
delete_button = QPushButton("删除")
delete_button.clicked.connect(self.delete_student)
avg_button = QPushButton("平均分")
avg_button.clicked.connect(self.calculate_average)
button_layout.addWidget(avg_button)
button_layout.addWidget(add_button)
button_layout.addWidget(delete_button)
layout.addLayout(button_layout)
def show_context_menu(self, pos):
"""显示右键菜单"""
context_menu = QMenu(self)
# 创建动作
edit_action = QAction("编辑", self)
delete_action = QAction("删除", self)
info_action = QAction("查看信息", self)
# 绑定事件
edit_action.triggered.connect(self.edit_student)
delete_action.triggered.connect(self.delete_student)
info_action.triggered.connect(self.show_student_info)
# 添加到菜单
context_menu.addAction(edit_action)
context_menu.addAction(delete_action)
context_menu.addSeparator()
context_menu.addAction(info_action)
# 显示菜单,在光标位置
context_menu.exec(self.table.mapToGlobal(pos))
def add_student(self):
"""添加学生数据"""
name = self.name_input.text().strip()
age = self.age_input.text().strip()
score = self.score_input.text().strip()
if not name or not age or not score:
QMessageBox.warning(self, "警告", "请填写完整信息")
return
try:
age = int(age)
score = float(score)
except ValueError:
QMessageBox.warning(self, "警告", "年龄和成绩必须是数字")
return
# 插入新行
row = self.table.rowCount()
self.table.insertRow(row)
self.table.setItem(row, 0, QTableWidgetItem(name))
self.table.setItem(row, 1, QTableWidgetItem(str(age)))
self.table.setItem(row, 2, QTableWidgetItem(str(score)))
# 清空输入
self.name_input.clear()
self.age_input.clear()
self.score_input.clear()
# 删除选中行
def delete_student(self):
"""删除选中行"""
# 获取选中的行索引
select_items = self.table.selectedItems()
if not select_items:
QMessageBox.warning(self, "警告", "请选择要删除的行")
return
selected_rows = set()
for item in select_items:
selected_rows.add(item.row())
for row in sorted(selected_rows, reverse=True):
self.table.removeRow(row)
QMessageBox.information(self, "信息", "删除成功")
def edit_student(self):
"""编辑选中行"""
# 获取选中的行索引
select_item = self.table.currentItem()
if select_item:
select_item.setFlags(select_item.flags() | Qt.ItemFlag.ItemIsEditable)
self.table.editItem(select_item)
def show_student_info(self):
"""查看选中行信息"""
# 获取选中的行索引
select_item = self.table.currentItem()
if select_item:
name = self.table.item(select_item.row(), 0).text()
age = self.table.item(select_item.row(), 1).text()
score = self.table.item(select_item.row(), 2).text()
QMessageBox.information(self, "信息", f"姓名: {name}\n年龄: {age}\n成绩: {score}")
def calculate_average(self):
"""计算平均分"""
total_score = 0
num_students = self.table.rowCount()
if num_students == 0:
QMessageBox.warning(self, "警告", "没有学生成绩")
return
for i in range(num_students):
total_score += float(self.table.item(i, 2).text())
average_score = total_score / num_students
QMessageBox.information(self, "信息", f"平均分: {average_score:.2f}")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StudentTable()
window.show()
sys.exit(app.exec())
效果演示:

🔍 四、总结与要点
| 概念 | 功能 | 关键方法 |
|---|---|---|
QMenu |
菜单容器 | .addAction() .exec() |
QAction |
菜单项 | .triggered.connect() |
| 右键菜单策略 | 控制是否允许右键菜单 | .setContextMenuPolicy() |
| 获取全局坐标 | 把局部位置转为全局 | .mapToGlobal(pos) |
您的资助是我最大的动力!
金额随意,欢迎来赏!

浙公网安备 33010602011771号