先放张图,也许我们要的是右边的样子,而QT设计器设计出来是左边的样子,那怎么样稍做修改一下,实现右边的效果呢?
另外,据说,在很久以前,QT实际上是可以设计出右边的样子的,但后来QT设计器取消了这个功能.
基础知识QAction
一个action就是一个菜单项,或者是工具栏中的一个按钮,一个action里面可以保存文本,图标,快捷键,tooltips等等信息.
action根据他所在的位置(菜单或者工具栏),会随遇而安,显示出菜单的样子,或者工具栏中的按钮的样子.
用Designer设计出如下样子的菜单
默认的action名字是用数字命名的,看起来比较乱, 修改一下action的名字,修改方法如下
最终的修改效果如图所示
现在的效果就是开头你看到的,所有菜单项目前面都可以打勾勾,但是不能用圆点单选.
再修改一下menu的名字吧,这样如果有多个顶级菜单,好区分.
目前的效果如下:
好了,现在主角出场,QActionGroup
QActionGroup就是能把一堆action归到一个组里面
如果调用他的方法setExclusive(true),那么同时就只能选中一个action.默认就是true
首先,我们来看看系统自动生成的代码(只关心左对齐,右对齐,居中对齐),在ui_mainwindow.h中
//创建left/right/center
action_align_left = new QAction(MainWindow);
action_align_left->setObjectName(QString::fromUtf8("action_align_left"));
action_align_left->setCheckable(true);
action_align_right = new QAction(MainWindow);
action_align_right->setObjectName(QString::fromUtf8("action_align_right"));
action_align_right->setCheckable(true);
action_align_center = new QAction(MainWindow);
action_align_center->setObjectName(QString::fromUtf8("action_align_center"));
action_align_center->setCheckable(true);
//把left/right/center加到样式菜单中
menu_style->addAction(action_align_left);
menu_style->addAction(action_align_right);
menu_style->addAction(action_align_center);
看到了吗?left/right/center被用addAction的方法加到菜单中,
我们需要中的是,把这三个action删除,然后把他们加到QActionGroup中,并把QActionGroup加到样式菜单中,
代码如下:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//先删除
menuBar()->removeAction(ui->action_align_left);
menuBar()->removeAction(ui->action_align_right);
menuBar()->removeAction(ui->action_align_center);
//再加回来
QActionGroup* alignGroup = new QActionGroup(this);
ui->menu_style->addAction(alignGroup->addAction(ui->action_align_left));
ui->menu_style->addAction(alignGroup->addAction(ui->action_align_right));
ui->menu_style->addAction(alignGroup->addAction(ui->action_align_center));
}
再点点鼠标,编辑一下slot,加上左对齐,右对齐,居中对齐的响应代码
void MainWindow::on_action_align_left_triggered()
{
ui->action_align_left->setChecked(true);
}
void MainWindow::on_action_align_right_triggered()
{
ui->action_align_right->setChecked(true);
}
void MainWindow::on_action_align_center_triggered()
{
ui->action_align_center->setChecked(true);
}
大功告成,看图:
思考:
本文描述的对齐菜单正好是菜单项中的最后三个菜单,
如果不是最后的三个菜单,怎么办呢?
removeAction好说,但remove后,怎么加进来呢?
我猜想,需要用到这个函数:
void insertAction(QAction *before, QAction *action)
第二个参数就是我们刚remove的action
那第一个参数又从哪里来呢?应该会用到
QList<QAction *> actions() const
先取出所有的action,找到三个菜单的最后一个菜单的下一个action,假设是X
然后在X前插入三个action就可以啦!
读者可以自己实践.
代码见 http://q1024.com/files/qt_window-master.zip 000300目录
[2020-12-14]增加
既然已经使用了ActionGroup,有一些细节需要知晓一下.
我们可以不用响应on_action_align_left_triggered/on_action_align_right_triggered/on_action_align_center_triggered,
而是增加一个slot
void on_action_alignment_triggered(QAction* action);
然后执行下面语句来连接slot
QObject::connect(alignGroup, SIGNAL(triggered(QAction *)), this, SLOT(on_action_alignment_triggered(QAction *)));
这样,只需要响应on_action_alignment_triggered就可以处理对齐的三个菜单了.
不过,因为on_action_align_left_triggered/on_action_align_right_triggered/on_action_align_center_triggered可以通过点击鼠标实现,所以多少还是比较方便.
您可以根据你自己的需求来决定用哪个slot来响应(或者都响应)
最后,我把代码和代码执行的时序贴出来:
void MainWindow::on_action_align_left_triggered()
{
qDebug() << "left " << ui->action_align_left->isChecked() << "\n";
//这个函数会执行,这条语句是没有意义的,因为用了QActionGroup,已经被选中好啦
ui->action_align_left->setChecked(true);
//对left对齐进行处理
}
void MainWindow::on_action_align_right_triggered()
{
//这个函数会执行,这条语句是没有意义的,因为用了QActionGroup,已经被选中好啦
ui->action_align_right->setChecked(true);
//对right对齐进行处理
}
void MainWindow::on_action_align_center_triggered()
{
//这个函数会执行,这条语句是没有意义的,因为用了QActionGroup,已经被选中好啦
ui->action_align_center->setChecked(true);
//对center对齐进行处理
}
void MainWindow::on_action_alignment_triggered(QAction* action)
{
if (action == ui->action_align_left)
{
qDebug() << "left alignment" << "\n";
}else if (action == ui->action_align_right)
{
qDebug() << "right alignment" << "\n";
}else if (action == ui->action_align_center)
{
qDebug() << "center alignment" << "\n";
}
}
执行结果为:
left true
left alignment
所以,on_action_align_left_triggered先执行,然后是on_action_alignment_triggered
细心的朋友可能会发现调试程序时,有如下输出
QMetaObject::connectSlotsByName: No matching signal for on_action_alignment_triggered(QAction*)
解决方法:
slot的名字不要叫on_action_xxxxxxx
要把on_action_alignment_triggered修改成OnAlignmentTriggered(或者其他什么名字,但格式不要是on_xxx_xxx)
这样就可以了
以下是原因,转载自https://blog.csdn.net/restraint/article/details/9956449
--------------------------------------------------------引用内容--开始-------------------------------------------------------
http://www.civilnet.cn/bbs/browse.php?topicno=4023
今天发现qt程序在运行时命令行窗口会弹出下面的警告:
QMetaObject::connectSlotsByName: No matching signal for ...
但实际功能并没有受影响。网上google了一圈,终于找到原因。
以下转自:http://www.qtforum.org/article/20685/connectslotsbyname.html
After a bit of sleuthing to find the source of all the Qt warnings, “QMetaObject::connectSlotsByName: No matching signal for …”, in our log window/file, here’s what I’ve found.
setupUi calls connectSlotsByName. connectSlotsByName parses the moc_ file in order to connect slots and signal s. The moc_ file contains a list of all the slots for the class. The parser iterates over the list of slot names looking for the following pattern: on_objectName_signal , where on_objectName_signal is the name of the slot, objectName is the object name and signal is the signal . For example, if you have a slot named, on_doneButton_clicked(), the parser looks for an object named doneButton, a signal named clicked and then connects the on_doneButton_clicked() slot to QButton’s signal clicked().
If you follow this naming convention, you do no t need to call the connect() method, no r do you need to connect the signal via the Qt UI editor in VisualStudio. connectSlotsByName will automatically make the connection for you.
So, in order to get rid of the “No matching signal for …” warnings, we need to either follow this naming convention, or make sure no ne of our slot names begin with “on_”. If you have a slot, onDoneButton_clicked, for example, connectSlotsByName will no try to connect it with a signal , no r will emit an warning.
上面大概就是 说:
用VistalStudio里的QtEditer可以自动调用Qt中“connectSlotsByName”即“按空间名称关联槽”的方式进行关 联,
对应的函数必须写成“on_控件名_信号名”的格式;
或者也可以通过connet函数人为显式地将信号和槽关联起来。
但是,如果采用显式 connect的方法的同时,又将槽函数的名字起成了“on_控件名_信号名”的格式,那么就会在运行时弹出 “QMetaObject::connectSlotsByName: No matching signal for”的警告了!
--------------------------------------------------------引用内容--结束-------------------------------------------------------
所以,解决方法就是修改一下slot的名字