类似上面的这种需求很普遍。比如,Win 7系统的资源管理器就提供了这种功能,一图以蔽之:
怎样在Qt中为一个窗体部件上实现context menu?我找到了一些资料:
[QT]创建鼠标右键菜单
How to add a list of QActions to QMenu and handle them with a single slot?
Right-click context menus with Qt
百度文库:QListWidget的item上实现右键菜单
我的测试代码:
主函数:
// main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
class QAction;
class QMenu;
class QTableWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onShowOrHideColumn(QAction *action);
private:
QTableWidget *stuInfoWidget;
QMenu *mainMenu;
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include
#include
#include
#include
#include
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QStringList columnNames;
columnNames << tr("Name") << tr("Sex") << tr("Age");
// 创建一个QTableWidget,共三列,分别显示:Name(姓名)、Sex(性别)、Age(年龄)
stuInfoWidget = new QTableWidget;
stuInfoWidget->setColumnCount(columnNames.size());
for (int i = 0; i < columnNames.size(); ++i) {
QTableWidgetItem *headerItem = new QTableWidgetItem(columnNames[i]);
stuInfoWidget->setHorizontalHeaderItem(i, headerItem);
}
// 创建一个菜单。通过这个菜单,可以选择显示/隐藏指定列。
mainMenu = menuBar()->addMenu(tr("Show or hide columns"));
for (int i = 0; i < columnNames.size(); ++i) {
QAction *action = new QAction(columnNames[i], this);
// 设定菜单项是可勾选的。
action->setCheckable(true);
// 设定菜单项初始状态是已被勾选的。
action->setChecked(true);
// 将列序号设定为菜单项的data,这样在槽函数onShowOrHideColumn中,
// 可以通过调用QAction::data方法来获知要显示/隐藏的列的序号。
action->setData(i);
mainMenu->addAction(action);
}
// 将mainMenu的triggered(QAction *)信号连接到自定义槽函数
// onShowOrHideColumn(QAction *action)上。这样,当用户触发mainMenu
// 中某一菜单项时,onShowOrHideColumn(QAction *)会被自动调用。
// QAction *类型的参数action指向被触发的菜单项。
connect(mainMenu, SIGNAL(triggered(QAction *)),
this, SLOT(onShowOrHideColumn(QAction *)));
// 给QTableWidget的header view添加context menu的一种方法。
QHeaderView *headerView = stuInfoWidget->horizontalHeader();
headerView->setContextMenuPolicy(Qt::ActionsContextMenu);
headerView->addActions(mainMenu->actions());
setCentralWidget(stuInfoWidget);
}
MainWindow::~MainWindow()
{
}
void MainWindow::onShowOrHideColumn(QAction *action)
{
// 稍后会添加该函数的实现代码。
}
How Qt Signals and Slots Work
接下来,该让onShowOrHideColumn做些什么了。起初,我的想法是,在槽函数中,通过action的isChecked方法来判断这个action是否已经checked。如果是,那么显示这个action所管理的列,然后调用action->setChecked(false)来取消这个action的checked状态;如果不是,则隐藏相应列,并setChecked(true)。具体代码如下:
void MainWindow::onShowOrHideColumn(QAction *action)
{
// 获取当前的checked状态。
bool isChecked = action->isChecked();
// 在构造函数中,我们创建QAction对象的时候,通过setData把
// 这个QAction的user data设置为其管理的列的序号。
// 这里,通过data方法取出这个列编号,然后调用setColumnHidden来显示/隐藏该列。
stuInfoWidget->setColumnHidden(action->data().toInt(),
isChecked);
// 设置新的checked状态。
action->setChecked(!isChecked);
}
void QAction::activate(ActionEvent event)
{
Q_D(QAction);
if(event == Trigger) {
QPointer guard = this;
if(d->checkable) {
// the checked action of an exclusive group cannot be unchecked
if (d->checked && (d->group && d->group->isExclusive()
&& d->group->checkedAction() == this)) {
if (!guard.isNull())
emit triggered(true);
return;
}
setChecked(!d->checked);
}
if (!guard.isNull())
emit triggered(d->checked);
} else if(event == Hover) {
emit hovered();
}
}
setChecked(!d->checked);
了解了这些后,重写这个槽函数为:void QAction::activate(ActionEvent event)
Sends the relevant signals for ActionEvent event.
Action based widgets use this API to cause the QAction to emit signals as well as emitting their own.
enum QAction::ActionEvent
This enum type is used when calling QAction::activate()
QAction::Trigger
this will cause the QAction::triggered() signal to be emitted.
void QAction::toggle() [slot]
This is a convenience function for the checked property. Connect to it to change the checked state to its opposite state.
void MainWindow::onShowOrHideColumn(QAction *action)
{
// 对于一个checkable的QAction对象,如果最初其checked属性:
// 1) == true, 那么在其被触发后,其checked属性将会在
// QAction::activate方法中被更改为false。因此,随后我们
// 在这个槽函数中调用isChecked时将得到false。
// 2) 和情况1)相反。
// 而且,我们无需自己调用setChecked方法来更新checked状态,因为
// QAction::activate已经帮我们做了这步工作。
stuInfoWidget->setColumnHidden(action->data().toInt(),
!action->isChecked());
}
StudentInfo_v1.zip
PS1:在使用Windows SDK编写Win32 GUI程序的时候,我们往往需要使用诸如CheckMenuItem、ModifyMenu、SetMenuItemInfo等这样的API来显式check或uncheck一个菜单项。
PS2:在MFC中,要更改一个菜单项的checked状态,需要为菜单项添加ON_UPDATE_COMMAND_UI消息映射,然后在消息映射函数中通过CCmdUI *类型的参数的setCheck方法来实现。
PS3:正是因为我在MFC & SDK中的编程经验,才使我写出了上面那个错误版本的槽函数。