How to add a QAction to a QListWidget - qt

I have the following code:
roslaserscandoialog.h
public:
explicit ROSLaserScanDialog(QWidget *parent = nullptr);
~ROSLaserScanDialog();
QListWidgetItem *createItemFromAction(const QAction* action);
private slots:
void on_listWidget_itemClicked(QListWidgetItem *item);
private:
Ui::ROSLaserScanDialog *ui;
QAction *mAddMsgs;
QAction *mDeleteMsgs;
roslaserscandoialog.cpp
ROSLaserScanDialog::ROSLaserScanDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ROSLaserScanDialog)
{
ui->setupUi(this);
connect(ui->listWidget,SIGNAL(on_listWidget_itemClicked(QListWidgetItem*)),this,SLOT(createItemFromAction(QListWidgetItem*)));
}
QListWidgetItem *ROSLaserScanDialog::createItemFromAction(const QAction *action)
{
Q_ASSERT( action );
QListWidgetItem *mAddMsgs = new QListWidgetItem();
mAddMsgs->setText( action->text() );
mAddMsgs->setToolTip( action->toolTip() );
mAddMsgs->setIcon( action->icon() );
// ...
return mAddMsgs;
}
void ROSLaserScanDialog::on_listWidget_itemClicked(QListWidgetItem *item)
{
mAddMsgs = new QAction(QIcon(":ros.png"), tr("Add New Message"), this);
mDeleteMsgs = new QAction(QIcon(":remove_item.png"), tr("Remove Message"), this);
}
What I have done so far:
I came across this post and also this one. Very useful as I set up my project in a very similar way, but nothing happens when I try to click on the QListWidget.
I know that in order to trigger the action I have to go to the slot called itemClicked as I did on the above code provided.
On the official documentation I was trying to apply what is advised but I don't know why nothing happens.
Please point to the right direction for solving this problem.

Look at console output, there should a warning about connect failing. If you look at your code, the reason should be pretty obvious. Consider
SLOT(createItemFromAction(QListWidgetItem*))
versus your method which isn't even a slot
QListWidgetItem *createItemFromAction(const QAction* action);
See the difference?
And then you have this slot:
void on_listWidget_itemClicked(QListWidgetItem *item);
which you are trying to use as a signal
SIGNAL(on_listWidget_itemClicked(QListWidgetItem*))
That obviously won't work.
It's a bit unclear what you want to happen when an item is clicked, but maybe you just should call createItemFromAction directly from the on_listWidget_itemClicked.
Additionally, add debug print or use breakpoint to verify that the on_listWidget_itemClicked is actually called when you click an item. If not, then you are missing connecting the relevant signal from your list view, ie. ui->setupUi(this); does not have that connect (in other words you did not do the connection in the GUI Designer).

Related

QMenu (and submenu) opens up in a different monitor location

I built an application that uses QMenu and its related submenu. The Qmenu is triggered to open via QAction.
The problem I have is that as soon as I call the QAction and I chose the QAction that carries the "Options", it should open the QMenu, but instead the QMenu open in another location of the application looking totally detached.
Below is the wrong behavior, where it is possible to see the QMenu opening in another location of the Desktop.
Here is the expected behavior where the QMenu opens up after selecting "Oprions":
I am not sure of why this is happening.
Below the snippet of code related:
rostreewidget.h
class ROSTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
enum ROSType {
NULLPTR,
TABLE,
BASE,
MENU
};
ROSTreeWidget(QWidget *parent = nullptr);
ROSType rostype(ROSTreeItem *rosItem) const;
ROSType rostype() const {
return rostype(currentItem());
}
signals:
void selectFrom();
void editLaserTable();
private:
QAction *mActionSELECT_FROM;
QAction *mActionEditLaserTable;
QAction *mAddMenuOptions;
QMenu *submenuLaserScanA;
QAction *submenuActionA, *submenuActionB, *submenuActionC;
QMenu *submenuLaserScanB;
QAction *submenuActionAA, *submenuActionBB, *submenuActionCC;
QMenu *submenuLaserScanC;
QAction *submenuActionAAA, *submenuActionBBB, *submenuActionCCC;
};
rostreewidget.cpp
ROSTreeWidget::ROSTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
mActionSELECT_FROM = new QAction(QIcon(":"), "SELECT * FROM", this);
mActionEditLaserTable = new QAction(QIcon(":"), "Edit LaserScan Table");
mAddMenuOptions = new QAction(QIcon(":"), "Options", this);
addActions({ mActionSELECT_FROM,
mActionEditLaserTable,
mAddMenuOptions});
connect(mActionSELECT_FROM, &QAction::triggered, [&]() {
emit selectFrom();
});
connect(mActionEditLaserTable, &QAction::triggered, [&]() {
emit editLaserTable();
});
connect(mAddMenuOptions, &QAction::triggered, [&]() {
mMenu = new QMenu();
submenuLaserScanA = mMenu->addMenu( "Menu A" );
mMenu->addSeparator();
submenuLaserScanB = mMenu->addMenu( "Menu B" );
mMenu->addSeparator();
submenuLaserScanC = mMenu->addMenu( "Menu C" );
submenuActionAAA = submenuLaserScanC->addAction("Setup C" );
submenuActionBBB = submenuLaserScanC->addAction( "Setup C" );
mMenu->show();
});
}
What I have done so far:
After following this source I was able to prepare the menu and the submenu to be inserted in the QAction and in fact all the related settings about it are as I need them to be.
I thought that linking the QAction inside the lambda function, exactly as I did for the other choices, would have worked, but instead what I am obtaining as soon as I launch the application is a QMenu that opens in another location of the Desktop without being linked to the main choice as shown below.
According to the documentation QMenu::addMenu parameters set are correct, so what is going on?
Thanks for pointing to the right direction for solving this issue.
Use this as a general pattern:
{
QMenu menu;
menu.addAction(...);
menu.addAction(...);
menu.exec(QCursor::pos()); // exec returns a triggered QAction
// process action
}
QMenu::exec() is modal. You don't need a QMenu instance outside the function.

QT - Cloning window "screen" in another window

I have two "QMainWindows" A and B.
A has a pointer to B as a class member (and creates B dynamically on constructor). The program implementation is in two different monitors. 'A' is shown on monitor 'X' and 'B' is shown on monitor 'Y'.
'A' controls what is shown on 'B', and since I have them in two different monitors, in 'A' I would like to see whats happening on 'B' (kind of a 'screen capture on real time'). Is there any way to do this?
I've thought to make a "QWidget *clone = B->ui->centralWidget;" on 'A's constructor, since in 'B' I have all the information under a 'QWidget centralWidget', but it doesn't seem to work.
Any illuminating idea would be deeply apreciated!
You should be able to use QPixmap::grabWindow and a QTimer. QPixmap::grabWindow returns a QPixmap that you can simply display on e.g. a QLabel
Dummy header file for the class that displays
class MainWindow: public QMainWindow, public Ui::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = NULL);
~MainWindow() {};
public slots:
void update();
private:
MainWindow2 *window;
QTimer *timer;
};
Implementation
MainWindow::MainWindow(QWidget *parent)
{
setupUi(this);
window = new MainWindow2;
window->show();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(5000);
}
void MainWindow::update()
{
if( window ) {
QPixmap pixmap = QPixmap::grabWindow( window->winId() );
label->setPixmap(pixmap);
}
}
EDIT: added a check to make sure that window is not NULL before asking for its window ID.
This way a screenshot of the child window will be shown on in a QLabel of the parent window and will be updated every 5 seconds (which you can obviously adjust to your needs). Does this cover your use case?

Display not updating on the QTimer?

Trying to display text based on when the QTimer fires off...
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_label1 = new QLabel("My Label not working", this);
QTimer* timerDisplay = new QTimer(this);
connect(timerDisplay, SIGNAL(Started()), this, SLOT(updateDisplay(this)));
timerDisplay->start(10);
}
void updateDisplay(MainWindow* m_this)
{
QString out;
out = "hello";
m_this->m_label1->setText("asdf");
}
connect(timerDisplay, SIGNAL(Started()), this, SLOT(updateDisplay(this)));
This statement is failing. And you're ignoring the message that Qt is printing on your console.
The problem is, you can't pass variables in connect statements like that. And what for, by the way? You can use this in the updateDisplay method without the need of passing it in explicitely!

Work around for signal and slot argument limitation

I want to make a PushButton when it is clicked, its text change into 'clicked'. I tried it by
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked(button1)));
where this refer to the MainWindow and
void MainWindow::markClicked(QPushButton *button) { button->setText("Clicked"); }
It does not seem to work because I think SLOT cannot take more arguments than SIGNAL. If there any approach to work around this limitation?
Thanks.
Qt signals/slots mechanism can only transfer signal to slot function with similar parameters. As a workaround, you should use QSignalMapper:
QSignalMapper mapper;
...
connect(button1, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(button1, button1); // not sure whether this is mandatory or not
...
connect(&mapper, SIGNAL(mapped(QWidget*)), this, SLOT(markClicked(QWidget*)));
and function markClicked is
void MainWindow::markClicked(QWidget *widget) {
QPushButton *button = qobject_cast<QPushButton*>(widget);
button->setText("Clicked");
}
The other way you could do this is to use a default value for the argument and then use the sender() method:
In MainWindow:
void markClicked(QPushButton *button = NULL);
then:
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked()));
and:
void MainWindow::markClicked(QPushButton *button) {
if (button==NULL) { button = qobject_cast<QPushButton*>(sender()); }
button->setText("Clicked");
}

how to add menu dynamically in Qt

I want to add, submenu to a menu item dynamically. How can I achive this?
I tried like this,
I have created an Action and submenu. Then I have added the submenu to action.
But, I have connected the “triggered” signal of action. its getting crash if I click on the action..
I have also handled the “aboutToShow” signal of menu, same its also getting crash when I click on action..
Here is the sampe code.
Submenu = new QMenu(this);
connect(Submenu, SIGNAL( aboutToShow()), this, SLOT(move ()));
QAction *test = new QAction(tr("Selection"), this);
test ->setMenu(Submenu);
menubar()->addAction(test);
I want to get the notification, before the display of submenu..
additonal information:
pleas try this code, in your main window constructor.
QAction *action = new QAction("Test",this);
QAction *dummyaction = new QAction("Testing",this);
QMenu *menu = new QMenu();
menu->addAction(dummyaction);
bool val= connect(menu, SIGNAL( aboutToShow()), this, SLOT( Move()));
val= connect(menu, SIGNAL( aboutToHide()), this, SLOT(Move()));
action->setMenu(menu);
this->menuBar()->addAction(action);
if I do like this, I am able to see one submenu item. But before that Move slot should call, its not getting called.. and even before hide also the same slot should call.. its not coming..
I tried the return values of connect.. its true only… so what is wrong with my code.. please say..
Such code should work:
QMainWindow wnd;
QAction *act = wnd.menuBar()->addMenu("SomeMenu")->addMenu("someSubmenu")->addAction("someAction");
QObject::connect(act,SIGNAL(triggered()),
someObj,SLOT(actionReaction()));
I think addMenu() addAction() should work in more reliable way. This approach works for me.
I'm not sure to understand exactly what you're willing to do into your Move() slot.
But here is your own code (I removed what seemed useless to me), modified so that it is not crashing on my computer :
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QAction>
#include <QMenu>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private:
QMenu* menu;
QAction *dummyaction;
QMenu* m_pSubMenu;
private slots:
void Move();
};
#endif // MAINWINDOW_H
mainwindow.cpp :
#include "mainwindow.h"
#include <QMenuBar>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_pSubMenu = NULL;
QMenuBar* pMenuBar = new QMenuBar(this);
setMenuBar(pMenuBar);
dummyaction = new QAction("Testing",this);
menu = new QMenu("Test", this);
menu->addAction(dummyaction);
this->menuBar()->addMenu(menu);
connect(menu, SIGNAL(aboutToShow()), this, SLOT(Move()));
}
void MainWindow::Move() {
if (!m_pSubMenu) {
m_pSubMenu = new QMenu(menu);
dummyaction->setMenu(m_pSubMenu);
}
QAction* pAction = new QAction("Test", this);
m_pSubMenu->addAction(pAction);
}
I don't know exactly what you want to do into your Move() slot, but as an example, each time the Move() slot is called, a new submenu item is added.
Hope this helps.

Resources