I am creating an app in qt, and i have come to a problem.
I have a qgraphics scene.
I create the scene and I have put some actions to take place when the user clicks
on a scene item.
I can detect the selectionChanged() signals, but:
The signal is emitted twice (once for the clicked item and once for the previously selected item (deselection), so the required actions take place twice for both items).
When an item is clicked, it remains selected and i can't click it again...
(i tried setting item->setSelected(false) but it gets in an infinite loop of selection/deselection...).
Anyone has any idea how to fix that?
What I am trying to achieve, is to have no action performed on deselection,
and to be able to re-click a clicked item and re-perform the action.
Define a slot yourSlot() and connect it to the signal selectionChanged(). In this slot you check if the item responsible for the signal emission is selected or not.
void yourSlot() {
QGraphicsItem *item = qobject_cast<QGraphicsItem *>(sender());
if (item) {
if ( item->isSelected() ) { //this item is selected
doSomethin();
//*deselect* the item so that it can be selected again
item->setSelected(false);
}
else { //the signal was fired because the item was deselected
//ignore()
}
}
}
I can't try it right now because I don't have Qt installed in this computer by I think it should work.
Related
I need to block specific buttons on an MMI.
I implemented a button blocking function in a subclass of QPushButton.
For this, I used the clicked() signal and blocked the button with blockSignals(true).
This means that with each button clicked on my MMI, 2 SLOTS are always called.
But when calling the blocking of a specific button, I get the first SLOT (clicked()) of my subclass, in which I block the button, then I then arrive in the original SLOT linked to this button, which is still called despite the blocking (the first time only).
How can I in my QPushButton subclass know the subsequent SLOTs linked to this button and avoid them (delete them)?
void QbtnStandardButton::slotButtonClicked(void)
{
if (modeProtection)
{
// Special mode to protect/unprotect the button
if (isProtected())
{
// Reset the protection
this->blockSignals(false);
}
else
{
// Set the protection: button will be unclickable
this->blockSignals(true);
}
modeProtection = false;
}
if (isProtected())
{
QMessageBox *pMsgBox = new QMessageBox(QMessageBox::Information,
"Protection",
"This button is protected!",
QMessageBox::Ok);
pMsgBox->exec();
pMsgBox->deleteLater();
// Here: remove subsequent SLOT of this button ?
}
}
I think it's very difficult if not impossible to find SLOTS linked to a button.
I worked around the problem by using an eventFilter() instead of a SIGNAL() in my base class.
In this case, I can filter the "clicked()" event before it is reissued.
for(auto s :listofPossibleValues){
// item =s;
action = myMenu.addAction("Set Value to "+s);
connect(action,SIGNAL(triggered(bool)),this,SLOT(menuClicked()));
}
void MainWindow::menuClicked(){
value = new QStandardItem(item);
model->setItem(mainindex->row(),mainindex->column(),value);
}
I add actions and connect signals to the slot to my menu using the code above. Previously I was using the item to be the text. But it will only work for last item.
Does anyone at least know how to get the action that I clicked on?
How can I make it work for each individual item rather than just the last one?
Use the triggered signal of QMenu:
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(menuClicked(QAction*)));
Then, in menuClicked():
void MainWindow::menuClicked(QAction *action) {
// do something with action
}
I want to have a line edit field in a popup menu I've got. I'm basically letting the user pick from one of several common sizes for something, but I want them to be able to enter a custom size as the last entry in the menu.
So I've got something like this (snipped from larger code, new_menu is the menu of interest):
QWidget *widget = new QWidget(new_menu);
QHBoxLayout *layout = new QHBoxLayout;
QLineEdit* le = new QLineEdit;
le->setPlaceholderText("Custom");
le->setFixedWidth(100);
ayout->addWidget(le);
widget->setLayout(layout);
QWidgetAction* wa = new QWidgetAction(new_menu);
wa->setActionGroup(group);
wa->setDefaultWidget(widget);
new_menu->addAction(wa);
connect(le, SIGNAL(returnPressed()), this, SLOT(leslot()));
Which works great, the LineEdit shows up nice and centered in the menu, it's got the placeholder text, I can click it and edit, everything. However, when I hit enter on the textBox, it emits the returnPressed signal and the menu emits a triggered signal with one of the other actions on the list, so at best I'm changing my configuration twice and at worst things break.
Additionally, when I click off the edge of the LineEdit (still in the menu though, but not in the editable area), the menu emits a triggered signal with the QWidgetAction associated with it, which isn't what I want.
So two questions:
1) Can I get the return to work the way I want. It's fine if the menu closes when it's hit, but it can't emit another action too.
2) Can I get it to not emit an action at all when the lineEdit is clicked?
Here's what I ended up doing for anyone that follows. I subclassed QLineEdit thusly:
class EnterLineEdit : public QLineEdit {
Q_OBJECT
public:
void keyPressEvent(QKeyEvent *evt) {
if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) {
emit returnPressed();
} else {
QLineEdit::keyPressEvent(evt);
}
}
};
This lets me manually emit the returnPressed signal when enter/return is hit and not pass it up the widget hierarchy, so the menu never sees it when enter is hit over the lineedit. I connected the returnPressed signal to the hide() slot of the menu so that the menu will still close, but without triggering an action.
I looked around and it seems that the problem is present not only for tree widget but also for other widgets. But in my case, I found a solution, although an incomplete one. I am adding actions to my tree widget, so that when you right click on it, a popup with these actions appears. However, when I add items to my tree widget and I right click on them, the same popup appears.
What I would like to do is that when you right click on the tree widget, a tree widget popup menu appears and when you right click on items, another corresponding popup menu appears. Does anybody knows how to do this?
First,config QTreeWidget to response(emit signal) right mouse click:
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
Second,connect the signal with your slot "MainWindow::prepareMenu":
connect(treeWidget,&QTreeWidget::customContextMenuRequested,this,&MainWindow::prepareMenu);
Third,create context menu in the slot:
void MainWindow::prepareMenu( const QPoint & pos )
{
QTreeWidget *tree = treeWid;
QTreeWidgetItem *nd = tree->itemAt( pos );
qDebug()<<pos<<nd->text(0);
QAction *newAct = new QAction(QIcon(":/Resource/warning32.ico"), tr("&New"), this);
newAct->setStatusTip(tr("new sth"));
connect(newAct, SIGNAL(triggered()), this, SLOT(newDev()));
QMenu menu(this);
menu.addAction(newAct);
QPoint pt(pos);
menu.exec( tree->mapToGlobal(pos) );
}
First you should set the context menu policy to CustomContextMenu:
treeView->setContextMenuPolicy(Qt::CustomContextMenu);
Then you can connect to the QWidget::customContextMenuRequested(const QPoint&) signal and show your context menu.
For those who prefer to use designer more, here is another way to do it:
1) Set context menu policy to custom context menu
Either by code:
ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
or using graphical designer, click on the tree widget and set it using Property Editor:
2) Create Handler function
In designer, right click on the treeWidget and select "Go to Slot..." option. A window similar to this will appear:
Click on the "CustomContextMenuRequested(QPoint)" option. Handler function will be defined, declared, and it will be connected automatically.
void MainWindow::on_treeWidget_customContextMenuRequested(const QPoint &pos)
{
// this function will be called on right click
}
This step can also be done by defining and connecting the slot function yourself.
3) Create the options on the context menu.
Go to action editor tab (Usually docked at the bottom of designer). Add actions you want to have on context menu by clicking new button on top left. You will encounter such an interface :
You can (optionally) have a tooltip or icon for the action, or make it checkable. You can crate a shortcut like Ctrl+C for a copy action.
4) Create the menu and fire it
void MainWindow::on_treeWidget_customContextMenuRequested(const QPoint &pos)
{
QMenu menu(this); // add menu items
menu.addAction(ui->actionDelete);
menu.addEdit(ui->actionDelete);
...
ui->actionDelete->setData(QVariant(pos)); // if you will need the position data save it to the action
menu.exec( ui->treeWidget->mapToGlobal(pos) );
}
5) Create handler functions for each action
Like in step 2, either create slot function and connect it manually, or right-click on on an action, click the "Go to slots..." option and select triggered() slot.
6) Lastly, apply your logic in the slot function
void MainWindow::on_actionEdit_triggered()
{
QTreeWidgetItem *clickedItem = ui->treeWidget->itemAt(ui->actionDelete->data().toPoint());
// your logic
}
Take a look at overloading QAbstractItemModel and providing your own OnContextMenuRequested. Via this function you can have different items create different context menus.
Here's some shortened pseudo-ish code from one of my projects that may be helpful:
void MyModel::OnContextMenuRequested(const QModelIndex& index, const QPoint& globalPos)
{
// find 'node' corresponding to 'index'
vector<pair<string,BaseNode*> > actions = node->GetActions(true);
if(actions.size()==0) return;
// the ptr list helps us delete the actions
boost::ptr_list<QObject> actionPtrList;
QList<QAction*> qtActions;
for(unsigned int i=0;i<actions.size();i++)
{
QAction* act = new QAction(actions[i].first.c_str(),NULL);
act->setData(qVariantFromValue(actions[i].second));
actionPtrList.push_back(act);
qtActions.append(act);
}
// create and show the context menu
QMenu *menu = new QMenu("Item actions",NULL);
actionPtrList.push_back(menu);
QAction* act = menu->exec(qtActions,globalPos);
if(act==NULL) return;
// act on the resulting action 'act'
}
I am adding custom widget to QListWidget.
My Custom Widget contains 2 Labels, 1 Button
/* My Custom Widget
UserDetails *myItem = new UserDetails(0," ALOA, Jeon");
UserDetails *myItem1 = new UserDetails(0," LOUIS, Stacy");
QListWidgetItem *item = new QListWidgetItem();
QListWidgetItem *item1 = new QListWidgetItem();
item->setSizeHint(QSize(0,45));
item1->setSizeHint(QSize(0,45));
ui->listWidget->addItem(item);
ui->listWidget->addItem(item1);
ui->listWidget->setItemWidget(item,myItem);
ui->listWidget->setItemWidget(item1,myItem1);
Using above Code I am adding mu Custom Widget item to QListWidget.
Now the problem is I am using One QPushButton in my CustomWidget now when i click on ListWidgetItem in ListWidget that button i want to change pressed state to some background-image and on release it should come back to normal state.For that I using stylesheet to do so but when i click on button it does not get click event of the List Widget (itemclicked SLOT of List) on double click it gets.
How to get on single click ?
This is not that simple, however it is doable. The problem is that when you click on the button the press event is not propagated to the list widget. So you have to find a way to propagate the signal back to the list widget.
When the button is pressed the pressed signal is emitted. When it is released the released signal is emitted. I will show you how to do it for one of them, you should do the same for the other.
The most straight forward way to do what you want is to add a connection between the pressed signal of the button and the slot which will modify the background of your list. What you have is a QListWidget in which there are some UserDetails widgets each of which has a QPushButton. So we have to go all the way up from the QPushButton to the QListWidget.
In your UserDetails class create a new signal, eg buttonPressed() and connect your button's pressed() signal with it. This way every time the button is clicked in your widget the widget itself will notify the world that its button has been clicked.
connect(ui->button, SIGNAL(pressed()), this, SIGNAL(buttonPressed())
Now we want to notify the QListWidget that the button has been pressed. In order to achieve this we have to connect the buttonPressed signal from the UserDetails with a slot, let's call the slot buttonPressedSlot()
connect(myItem, SIGNAL(pressed()), this, SLOT(buttonPressedSlot())
Now the slot should detect which is the sender (since all UserDetails objects will get connected to the same slot, find the corresponding QListWidgetItem and call the slot that will update the background of this item.
void LiwstWidgetClick::buttonPressedSlot()
{
QObject* signalSender = QObject::sender();
UserDetails* p = qobject_cast<UserDetails *>(signalSender);
if (p == NULL)
return;
// Get Position in list widget
for (unsigned i=0; i<ui->listWidget->count(); i++)
{
QListWidgetItem* item = ui->listWidget->item(i);
if (ui->listWidget->itemWidget(item) == p)
cellClicked(item); // THIS IS YOUR FUNCTION THAT CHANGES THE
// BACKGROUND OF THE GIVEN ITEM
}
}
You should do the same for the released() signal.
EDIT
If the button was public you could avoid the extra signal, eg if you had a function (getButton()) in your UserDetails that returned the ui->button you could have just one connection
connect(myItem->button(), SIGNAL(pressed()), this, SLOT(buttonPressedSlot()));
EDIT 2
If you just want to change the background of you button when it is pressed then you can achieve it using stylesheets. You need to entries in your stylesheet one for the normal button state and one for the pressed state. For a list of available states have a look here. Sample code follows
QListView QPushButton {
color: grey;
border-image: url(/path/to/image1) 3 10 3 10;
}
QListView QPushButton:pressed {
color: red;
border-image: url(/path/to/image2) 3 10 3 10;
}
Notice that I use the QListView Descendant Selector in order to get only these QPushButtons that are children of a QListView