QComboBox Get The Varient When "currentIndexChanged(int)" Emitted - c++

I am having difficulty finding documentation on this or an example.
Could someone concretely show me how to access the QVariant of the currently selected index in a QComboBox
QComboBox * combo = new QComboBox();
combo->addItem("Bla1", QVariant(1));
combo->addItem("Bla2", QVariant(2));
combo->addItem("Bla3", QVariant(3));
combo->addItem("Bla4", QVariant(4));
connect(combo, SIGNAL(currentIndexChanged(int)), this, slot(HANDLEITMAN(int))
And of course else where in the source
void TheCooler::HANDLEITMAN(int index)
{
//What do I do with index?
//sender()?
}

First, make combo a member of TheCooler, or otherwise put HANDLEITMAN in a class which has combo as a member. Unless it's available to TheCooler::HANDLEITMAN somehow you can't get the data, and this is the logical way to do it. Then it's just
void TheCooler::HANDLEITMAN(int index)
{
QVariant data = combo->itemData(index);
}

If you don't want to make combo a member of the class TheCooler, you can use the sender() function that returns a pointer to the QObject that sent the triggering signal (in this case, currentIndexChanged(int)).
void TheCooler::HANDLEITMAN(int index)
{
QComboBox * combo = qobject_cast< QComboBox * >(sender());
if (combo == 0)
return; // something wrong happened
QVariant data = combo->itemData(index);
}
If combo is null, then you probably tried to call the slot by yourself, or you have connected it with a signal emitted by a class that is not a QComboBox.

Related

Disable QAction if the QTreeView item has no children

I have QTreeView populated from database.
I have contextmenu configured as:
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
I have the method to look for a right click to open contextMenu on the item.
void MainWindow::on_treeView_customContextMenuRequested(const QPoint &pos)
{
QModelIndex idx = ui->treeView->indexAt(pos);
if (!idx.isValid())
{return;}
else{
QPoint globalPos = ui->treeView->mapToGlobal(pos);
QAction* selectedItem = contextMenu->exec(globalPos);
if (selectedItem){
qDebug () << selectedItem;
}
}
h.file
QMenu *contextMenu;
How do I check if the selected item from QTreeView is not a parent of any item & it has a parent.
Should I include QTreeView and QStandardItem code here to see or that's irrelevant?
The Qt doc. has a dedicated chapter for this topic:
Model/View Programming
which I recommend to get an overview.
Concerning the actual question of OP:
How do I check if the selected item from QTreeView is not a parent of any item & it has a parent.
The QTreeView inherits QAbstractItemView::model() which provides a pointer to the QAbstractItemModel which in turn provides the underlying model data for the rendered tree view items.
Any provided QModelIndex in a view should refer to this model.
The QAbstractItemModel provides a variety of methods to retrieve data concerning visualization and relations of model items. The QTreeView uses this but it should be used as well for any added function.
So, selected item is not parent of any item is turned around into "selected item has no children" for which QAbstractItemModel::hasChildren() is good for:
bool QAbstractItemModel::hasChildren(const QModelIndex &parent = QModelIndex()) const
Returns true if parent has any children; otherwise returns false.
Use rowCount() on the parent to find out the number of children.
Note that it is undefined behavior to report that a particular index hasChildren with this method if the same index has the flag Qt::ItemNeverHasChildren set.
Note: This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.
See also parent() and index().
and it has a parent can be retrieved using QAbstractItemModel::parent():
QModelIndex QAbstractItemModel::parent(const QModelIndex &index) const
Returns the parent of the model item with the given index. If the item has no parent, an invalid QModelIndex is returned.
A common convention used in models that expose tree data structures is that only items in the first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0.
When reimplementing this function in a subclass, be careful to avoid calling QModelIndex member functions, such as QModelIndex::parent(), since indexes belonging to your model will simply call your implementation, leading to infinite recursion.
Note: This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.
See also createIndex().
Putting this together, OPs function should look like this:
void MainWindow::on_treeView_customContextMenuRequested(const QPoint &pos)
{
QModelIndex idx = ui->treeView->indexAt(pos);
if (!idx.isValid()
|| !ui->treeView->model()->hasChildren(idx)
&& !ui->treeView->model()->parent(idx).isValid()) {
return;
// bail out -> no context menu for leaf nodes or toplevel nodes
} else {
QPoint globalPos = ui->treeView->mapToGlobal(pos);
QAction* selectedItem = contextMenu->exec(globalPos);
if (selectedItem) {
qDebug () << selectedItem;
}
}
}
I'm not quite sure whether this matches exactly OPs required behavior. It might be necessary to fix the condition but this should be not that hard.

Qt adding "action" to dynamically added QWidget

In my ui I have a button, which adds QComboBox (with some items) and QLabel(with some text) when clicked. Variable "index" is the number of added QComboBoxes and QLabels. "ol" is qvector with some data.
void MainWindow::on_iAddOtherButton_clicked()
{
QComboBox *p1 = new QComboBox(this);
p1->setObjectName("comboBox"+QString::number(index));
QLabel *p2 = new QLabel(this);
p2->setObjectName("othLabel"+QString::number(index));
for(int i = 0; i < static_cast<int>(ol.size()); ++i){
p1->addItem(ol.at(i).getName());
ui->otherLayout->addWidget(p1,index+1,0);
}
p2->setText(...some text...));
ui->otherLayout->addWidget(p2,index+1,1);
index++;
}
And this works well, in the layout they are in pairs like this:
QComboBox1 QLabel1
QComboBox2 Qlabel2
Now I want to change value of the QComboBox1, after which, text of the QLabel1, will automatically change to something else.
I tried to do this with connect, but QComboBox on currentTextChanged() emits only QString with a new value. Is there some way to emit name of object + new value?
Or there is some completely other solution to do this?
From within your slot, you can call sender() to get a pointer to the sender of the signal. From there you can decide what to do next based on sender()->objectName().
You might also consider using setProperty(...) on the combobox objects to store an index to a vector of pointers to QLabel instances with the vector being a class member. Then you could retrieve the index inside your slot by calling sender()->property(...) and use it to access the correct QLabel widget from the vector.

Is there a way to know what activated QAction?

I have created instance of QAction inside QGraphicsView child class and connected it to my slot in the same class.
QAction *action = new QAction(tr("New"), this);
action->setObjectName("addStopAction");
action->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_N));
connect(action, SIGNAL(triggered()), this, SLOT(addNew()));
addAction(action);
Slot is a function creating new instance of QGraphicsItem on scene assigned to QGraphicsView.
void MyGraphicsView::addNew() {
// Insert new item at cursor position
}
I also add this action to a QMenu which serves as my class context menu.
QMenu *contextMenu = new QMenu(this);
contextMenu->addAction(action);
Everything works fine. When I press Command/Ctrl + N new item is created at cursor position. But when I right-click and select action from context menu I want new item to be created at menu positon.
I can, of course, do some little hack to flag if SLOT was called after contextMenuEvent or something like that, but what I would like to know is:
Is there any way to find out what made QAction emit its triggered() signal inside connected SLOT? That way I could handle when I should place new item at cursor position and when at context menu position inside SLOT implementation.
Of course, you can find out what signal emit inside connected SLOT.
Just use QObject::sender(). In you case:
void MyGraphicsView::addNew() {
QAction* pAction = qobject_cast<QAction*>(sender());
Q_ASSERT(pAction);
// do something with pAction
}
I think you can use custom data that a QAction object can contain.
You can set it when you create a context menu:
void showContextMenu(const QPoint &pos)
{
...
action->setData(pos);
...
}
And in the addNew() function you check if data exists and reset it in the end:
void addNew()
{
QPoint pos;
QPoint posFromAction = action->data()->toPoint();
if (posFromAction.isNull())
{
pos = QCursor::pos(); ///< pos will be current cursor's position
}
else
{
pos = posFromAction; ///< pos will be menu's position
}
doYourStuffAt(pos)
action->setData(QPoint()); ///< reset action's data
}
i managed something similar by connecting the menu to a function like connect (menu, SIGNAL( triggered(QAction*) ), this, SLOT( menuAction_triggered(QAction*) ));
when you execute you context menu, the QMenu::exec(QPoint) will return you the pointer to the action, so you may not need a extra function/slot for it.
you can check for the name of the action with its text QAction::text() or if you have stored your pointers somewhere by comparing the address.
soo long zai
You can reference self.sender() in the function that is called when by customContextMenuRequested signal.
self.tree1.customContextMenuRequested.connect(self.menu1pop) # rightclick menu signal
...
def menu1pop(self, pos):
widget = self.sender()
item = widget.itemAt(pos)
if item is None: return # only show contextmenu if on an item
self.setProperty("mywidget", widget) # pass current widget
self.menu1.popup(QCursor.pos()) # show menu at right click cursor position
self.sender() will return your widget object and then you can set the widget object inside a property.
Then when your action function is called you can recall the widget object by reading the property.
widget = self.property("mywidget")
It's a bit of a hack, but a simple and reliable way to know which widget your action was called from.
You MIGHT be able to use QObject::sender() in the slot that receives the call. Not tried this for actions though. It's probably a bit uglier than your proposed 'hack' (where you could actually implement that quite nicely with a scoped class).

How to add a list of QActions to a QMenu and handle them with a single slot?

First, I have a list of QWidgets that I won't know the length of until runtime. I then create a QListWidget where I show them and when someone clicks them I use the signal currentItemChanged(QListWidgetItem*, QListWidgetItem*) to catch it and get the clicked item's index.
Now I want to do a similar thing in the QMenu. I will know the list when the QMenu and its actions get built, but I won't be able to hard code this.
How can I create actions, catch their signals and connect them to the same slot which does different things depending on the action's position (index) in the menu list? There must be some way to solve this since other applications use this. I tried to look at mapping but I couldn't get my head around how to use it for this.
I tried to grab the sender in the slot but was not able to get any useful information from it.
You can associate an index (or any other data) to each action when they are created with QAction::setData and connect the signal QMenu::triggered(QAction*) to your slot.
You'll then be able to retrieve the data through the QAction::data() function of your slot parameter.
MyClass::MyClass() {
// menu creation
for(...) {
QAction *action = ...;
action->setData(10);
...
menu->addAction(action);
}
// only one single signal connection
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(mySlot(QAction*)));
}
void MyClass::mySlot(QAction *action) {
int value = action->data().toInt();
}
Other methods: signal mapping or the use of sender(), are explained in that article of Qt Quaterly.
A more generic (not specific to QMenu) way to approach this is the QActionGroup class. This allows you to isolate specific menu items as a related group, or group different widgets together.
void MyClass::InitMenu(QMenu* menu)
{
QActionGroup* actions1 = new QActionGroup(menu);
actions1->setExclusive(false);
actions1->addAction(menu->addAction(tr("Action1")))->setData(1);
actions1->addAction(menu->addAction(tr("Action2")))->setData(2);
actions1->addAction(menu->addAction(tr("Action3")))->setData(3);
actions1->addAction(menu->addAction(tr("Action4")))->setData(4);
actions1->addAction(menu->addAction(tr("Action5")))->setData(5);
connect(actions1, SIGNAL(triggered(QAction*)), SLOT(MySlot(QAction*)));
QActionGroup* actions2 = new QActionGroup(menu);
actions2->addAction(menu->addAction(tr("Undo Action1")))->setData(1);
actions2->addAction(menu->addAction(tr("Undo Action2")))->setData(2);
//...
connect(actions2, SIGNAL(triggered(QAction*)), SLOT(MyUndoSlot(QAction*)));
}
and in the slot:
void MyClass::MySlot(QAction* triggeredAction)
{
// use either the action itself... or an offset
int value = triggeredAction->data().toInt()
}
You can also have a QMap of QActions and ints and as soon as you add your action to the menu you can also add it to your map with a value that is +1 different from the previous one. You can then wire QAction::triggered to a generic slot, from where you can get the sender of the signal by calling sender(), dynamic cast it to a QAction and then look up with value in your map:
class MyClass {
public:
void Init();
private slots:
void onTriggered();
private:
QMap<QAction*, int> _actionToInt;
}
MyClass::Init() {
QMenu* menu = new QMenu();
// Loop for illustration purposes
// For general purpose keep an index and increment it every time you add
for(int i=0; i<10; ++i) {
QAction* action = menu->addAction("Item1");
_actionToInt.insert(action, i);
connect(action, &QAction::triggered, this, &MyClass::onTriggered);
}
}
void MyClass::onTriggered() {
QAction* action = qobject_cast<QAction*>(sender());
//For safety purposes
if (action && _actionToInt.contains(action) {
//And here you have your index!
int index = _actionToInt.value(action);
}
}

Get previous value of QComboBox, which is in a QTableWidget, when the value is changed

Say I have a QTableWidget and in each row there is a QComboBox and a QSpinBox. Consider that I store their values is a QMap<QString /*Combo box val*/,int /*spin box val*/> theMap;
When comboBoxes value or spin boxes value is being changed I want to update theMap. So I should know what was the former value of the combo box in order to replace with the new value of the comboBox and also take care of the value of the spin box.
How can I do this?
P.S. I have decided to create a slot that when you click on a table, it stores the current value of the combo box of that row. But this works only when you press on row caption. In other places (clicking on a combobox or on a spinbox) itemSelectionChanged() signal of QTableWidget does not work.
So in general my problem is to store the value of the combo box of selected row, and the I will get ComboBox or SpinBox change even and will process theMap easily.
How about creating your own, derived QComboBox class, something along the lines of:
class MyComboBox : public QComboBox
{
Q_OBJECT
private:
QString _oldText;
public:
MyComboBox(QWidget *parent=0) : QComboBox(parent), _oldText()
{
connect(this,SIGNAL(editTextChanged(const QString&)), this,
SLOT(myTextChangedSlot(const QString&)));
connect(this,SIGNAL(currentIndexChanged(const QString&)), this,
SLOT(myTextChangedSlot(const QString&)));
}
private slots:
myTextChangedSlot(const QString &newText)
{
emit myTextChangedSignal(_oldText, newText);
_oldText = newText;
}
signals:
myTextChangedSignal(const QString &oldText, const QString &newText);
};
And then just connect to myTextChangedSignal instead, which now additionally provides the old combo box text.
I hope that helps.
A bit late but I had the same problem and solved in this way:
class CComboBox : public QComboBox
{
Q_OBJECT
public:
CComboBox(QWidget *parent = 0) : QComboBox(parent) {}
QString GetPreviousText() { return m_PreviousText; }
protected:
void mousePressEvent(QMouseEvent *e)
{
m_PreviousText = this->currentText();
QComboBox::mousePressEvent(e);
}
private:
QString m_PreviousText;
};
My suggestion is to implement a model, which would help you make a clean separation between the data, and the UI editing the data. Your model would then get notified that a given model index (row and column) changed to the new data, and you could change whatever other data you needed to at that point.
I was just having a similar issue, but for me i needed the previous index for something very trivial so defining and implementing a whole class for it was unjustified.
So what I did instead was keep an argument called say 'previousIndex' and updated it's value only after I had done everything I needed with it