Slot is not being used - c++

I'm learning the signals/slots in Qt and I have found a problem. I need to create my own slot that is called when items on QGraphicsScene (in QGraphicsView) are moved or selected.
I'm starting with a simple app that has one widget and on it is graphicsView and label. I've created a slot in my window and connected it to QGraphicsScene's signal, but it is not being used. Where is my mistake?
Here is the code:
//MainWindow.h
//as generated by QtCreator, just added one slot to it
...omitted for brevity...
public slots:
void selectedItemChanged(QGraphicsItem * newItem, QgraphicsItem * oldItem);
..omitted for brevity...
//------------------------------------------------------------------
//MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene * scene = new QGraphicsScene();
scene->setBackgroundBrush (QBrush(Qt::gray));
ui->graphicsView->setScene (scene);
for(int x = 10; x < 250; x+=20)
{
QGraphicsEllipseItem * item = scene->addEllipse (x,x,5,5,QPen(Qt::darkGreen),QBrush(Qt::darkGreen));
item->setFlag (QGraphicsItem::ItemIsFocusable,true);
}
QObject::connect (scene,SIGNAL(focusItemChanged),this,SLOT(selectedItemChanged));
}
void MainWindow::selectedItemChanged (QGraphicsItem *newItem, QGraphicsItem *oldItem)
{
qDebug()<<"called";
if(newItem == 0)
{
ui->label->setText ("Není vybrán bod");
}
else
{
ui->label->setText (QString::number (newItem->scenePos ().x ()) + "," + QString::number (newItem->scenePos ().y ()));
}
}
Now, when I run the probram it rins ok, but I cannot set Focus on the circles(ellipses) drawn on the scene and the slot is not used. I tried setting IsSelectable flag, but it does not help. Is there any other preferred way to get this done or solution to my problem?

You're not linking against the signal's right signature, according to the documentation:
void QGraphicsScene::focusItemChanged( QGraphicsItem * newFocus, QGraphicsItem * oldFocus,
Qt::FocusReason reason)
and also notice that you can check the connection's success/failure status via the bool return type of the QObject::connect method

So, in the end i found the answer to my own question. It was a mistake on my side.
in the connect() i used the slots without parenthesis/parameters. It should have looked like:
QObject::connect (scene,
SIGNAL(focusItemChanged(QGraphicsItem*,QGraphicsItem*,Qt::FocusReason)),
this,
SLOT(selectedItemChanged(QGraphicsItem*,QGraphicsItem*)));

Related

QT doesn't properly processes events in loop

I want to repeat the action performed in a SLOT until a qpushbutton is down.So I've done the following connections:
connect(ui->button,SIGNAL(pressed()),this,SLOT(button_hold()));
connect(ui->button,SIGNAL(released()),this,SLOT(button_released()));
and I've implemented the SLOTS in the following way
void My_class::button_hold(){
//CLASS ATTRIBUTE key_is_released , i
QThread::msleep(200);
int wait_lock = 500;
i++; //GOAL OF THE SLOT
QCoreApplication::processEvents(QEventLoop::AllEvents);
while(!key_is_released){
QThread::msleep(wait_lock);
i++;
cout<<i<<endl;
if(wait_lock > 50) wait_lock -= 50;
QCoreApplication::processEvents(QEventLoop::AllEvents);
}
key_is_released = false;
}
void My_class::button_released(){
key_is_released = true;
}
The goal is to repeat the action in the button_hod() Slot (In the example it's to increase i) even more quickly decreasing wait_lock and keeping the button down.The issue is that if i click button two times or more in a quick way the loop never ends,like if processEvents() would not work.The firts msleep is used in order to do not enter the loop (i is increased just once)if the button is just clicked and it is not keep down too long.If i do not click quickly but I keep down the button the method works well.
What am I doing wrong?
Reentering the event loop is a bad idea, and leads to spaghetti code.
Qt provides a wonderful state machine system with UML semantics. It's often best to model the system you're designing as a state machine. Here, there are two states: an idle state, and an active state: the value is incremented periodically while active.
The state machine decouples the Ui particulars (a button) from the core functionality: that of a periodically incrementing value.
Ideally, you would also factor out the state machine and the value to a controller class, and only connect the controller and the Ui from main() or a similar function.
// https://github.com/KubaO/stackoverflown/tree/master/questions/48165864
#include <QtWidgets>
#include <type_traits>
class Ui : public QWidget {
Q_OBJECT
int m_value = -1;
QStateMachine m_machine{this};
QState m_idle{&m_machine}, m_active{&m_machine};
QVBoxLayout m_layout{this};
QPushButton m_button{"Hold Me"};
QLabel m_indicator;
QTimer m_timer;
void setValue(int val) {
if (m_value == val) return;
m_value = val;
m_indicator.setNum(m_value);
}
void step() {
if (m_value < std::numeric_limits<decltype(m_value)>::max())
setValue(m_value + 1);
}
public:
Ui(QWidget * parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_button);
m_layout.addWidget(&m_indicator);
m_machine.setInitialState(&m_idle);
m_idle.addTransition(&m_button, &QPushButton::pressed, &m_active);
m_active.addTransition(&m_button, &QPushButton::released, &m_idle);
m_machine.start();
m_timer.setInterval(200);
connect(&m_timer, &QTimer::timeout, this, &Ui::step);
connect(&m_active, &QState::entered, [this]{
step();
m_timer.start();
});
connect(&m_active, &QState::exited, &m_timer, &QTimer::stop);
setValue(0);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"
The problem was that clicking fast let say 10 times ,the release slot was called yet 10 times before entering in the loop, so key_is_released would be never updated again to true.Instead the button_hold slot was not yet been called for ten times.So adding a counter, increasing it in the button_hold slot and decreasing it in the release slot and adding a second condition counter>0 besides !key_is_released in the while loop fixes all.(It enters the loop only if not all the release slot have been run)

Is it possible to call a slot when any of the widgets in a dialog box emits a signal?

I am trying to create a configuration menu box for an application, and have used a QDialog box to display the options that the user can change. This box contains QComboBoxes and QLineEdits, but a lot of them (7 combo boxes and 12 line edits). There is a QPushButton in the bottom called "Apply Changes" that should get enabled only when any property in the box gets changed.
Do I have to link every signal from each widget with a slot to enable the button individually or is there a signal that the QDialog box itself emits when there is a change in its constituent widgets?
Right now I have this:
connect(Combo1,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)));
connect(Combo2,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)))
followed by 17 more lines of these connections.
void MyClass::fnEnable(QString)
{
ApplyButton->setEnabled(true); //It is initialised as false
}
I was wondering if there was a shorter way of doing this, maybe (like I mentioned before) a signal emitted by QDialog (I couldn't find one in the documentation)
I know that this does not speed up the program, as only the required connection is called, but it would make any further attempts at making more ambitious dialog boxes easier.
Actually there is no such signal, but one approach is to create a list of QComboBox, and make the connections with a for, for example:
QList <*QCombobox> l;
l<<combobox1<< combobox2<< ....;
for (auto combo: l) {
connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}
The same would be done with QLineEdit.
You can iterate over an initializer list of widgets boxes and leverage C++11 to do all the boring work for you:
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
auto const comboBoxes = {Combo1, Combo2, ... };
for (auto combo : comboBoxes)
connect(combo, &QComboBox::activates, this, &MyClass::fnEnable);
}
You can also automatically find all the combo boxes:
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
ui.setupUi(this); // or other setup code
for (auto combo : findChildren<QComboBox*>(this))
connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}
Or you can automatically attach to the user property's change signal. This will work on all controls that have the user property. The user property is the property of a control that contains the primary data the control is displaying.
void for_layout_widgets(QLayout * layout, const std::function<void(QWidget*)> & fun,
const std::function<bool(QWidget*)> & pred = +[](QWidget*){ return true; })
{
if (!layout) return;
for (int i = 0; i < layout->count(); ++i) {
auto item = layout->itemAt(i);
for_layout_widgets(item->layout(), fun, pred);
auto widget = item->widget();
if (widget && pred(widget)) fun(widget);
}
}
class MyClass : public QWidget {
Q_OBJECT
Q_SLOT void MyClass::fnEnable(); // must take no arguments
...
};
MyClass::MyClass(QWidget * parent) : QWidget(parent) {
// setup code here
auto slot = metaObject()->method(metaObject()->indexOfMethod("fnEnable()"));
Q_ASSERT(slot.isValid());
for_layout_widgets(layout(), [=](QWidget * widget){
auto mo = widget->metaObject();
auto user = mo->userProperty();
if (!user.isValid()) return;
auto notify = user.notifySignal();
if (!notify.isValid()) return;
connect(widget, notify, this, slot);
});
}
You can also keep the combo boxes in an array, by value. This minimizes the costs of indirect references and results in code that will take the least amount of memory possible and perform well:
class MyClass : public QWidget {
Q_OBJECT
QVBoxLayout m_layout{this};
std::array<QComboBox, 14> m_comboBoxes;
...
};
MyClass(QWidget * parent) : QWidget(parent) {
for (auto & combo : m_comboBoxes) {
m_layout.addWidget(&combo);
connect(&combo, &QComboBox::activates, this, &MyClass::fnEnable);
}
}

How to clear a label in qt creator

this is the first time i write in this site, I'm trying approach me at Qt-creator but I've a problem:
I want to delete the text of the label when the user click a button, i've tried some solution but without success
this is the code:
struct finestra{
float costo;
int altezza;
int larghezza;
QString text;
QString costoStr;
};
float Totale=0;
finestra vet[21];
int i=1;
//SOME CODE
Totale+=vet[i].costo;
vet[i].costoStr = QString::number(vet[i].costo);
vet[i].text = vet[i-1].text + "Finestra ad un anta bianca <br>" + "€" + vet[i].costoStr +"<br>";
ui->TotaleFinestre->setText(QString(vet[i].text));
i++;
I've tried with this function:
void preventivi::on_pushButton_clicked()
{
ui->TotaleFinestre->clear();
}
if someone know how to do please answer,
thanks to all and sorry for my bad english.
Maybe you should try
void preventivi::on_pushButton_clicked()
{
ui->TotaleFinestre->setText("");
}
As QLabel define the slot void QLabel::clear(), you can also just connect this slot with the clicked() signal that will be emitted after a click on your pushButton, using the QObject::connect method :
QObject::connect(pointer_to_your_pushButton, SIGNAL(clicked()), pointer_to_your_label, SLOT(clear()));
EDIT : Here is a small example
The UI is a QWidget that has a QLabel and a QPushButton. I did that with Qt Designer but it doesn't matter here.
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QObject::connect(ui->pushButton, SIGNAL(clicked()), ui->label, SLOT(clear()));
}
Widget::~Widget()
{
delete ui;
}
You can even do that using "Edit Signals/Slots" inside Qt Designer and make the signal/slot connection between your widgets. ( you won't need to manually call the previous QObject::connect, as it will be done automatically inside the Ui_Widget class, generated by the uic)
Or you can do all without Qt Designer, it's up to you.
Hope this helps.

Qt RightClick on QListWidget Opens Contextmenu and Delete Item

I want to know how can I open a popup menu when I right click on the table items. In the popup menu some actions like add and delete should be given, which will create a new row or delete the selected row.
I am a new in the Qt world, so if anybody can give me the full details (with code if possible) then I will be really grateful towards him/her.
Thank you.
My goal: Only in the area of QListWidget and only if you click on an item, the menu with Delete will be opened.
Edit : Ok I solved the problem with the QListWidget and the menu. Now the following must be accomplished:
If you click on an item with the right mouse button, and then click Delete, then the item will be deleted.
My code:
void ProvideContextMenu(const QPoint &); // MainWindow.h
// In MainWindow.cpp
ui->listFiles->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->listFiles, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(ProvideContextMenu(const QPoint &)));
void MainWindow::ProvideContextMenu(const QPoint &pos)
{
QPoint item = ui->listFiles->mapToGlobal(pos);
QMenu submenu;
submenu.addAction("ADD");
submenu.addAction("Delete");
QAction* rightClickItem = submenu.exec(item);
if (rightClickItem && rightClickItem->text().contains("Delete") )
{
ui->listFiles->takeItem(ui->listFiles->indexAt(pos).row());
}
}
Edit2 : Ok I solved the whole problem :D. I uploaded my code, if somebody needs something like that it can help him/her.
Firstly you need to create slot for opening context menu:
void showContextMenu(const QPoint&);
At constructor of your class, which used QListWidget, set context menu policy to custom and connect QListWidget::customContextMenuRequested(QPoint) signal and showContextMenu() slot like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(listWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
}
Then need to realize context menu opening:
void MainWindow::showContextMenu(const QPoint &pos)
{
// Handle global position
QPoint globalPos = listWidget->mapToGlobal(pos);
// Create menu and insert some actions
QMenu myMenu;
myMenu.addAction("Insert", this, SLOT(addItem()));
myMenu.addAction("Erase", this, SLOT(eraseItem()));
// Show context menu at handling position
myMenu.exec(globalPos);
}
After this we need to realize slots for adding and removing QListWidget elements:
void MainWindow::eraseItem()
{
// If multiple selection is on, we need to erase all selected items
for (int i = 0; i < listWidget->selectedItems().size(); ++i) {
// Get curent item on selected row
QListWidgetItem *item = listWidget->takeItem(listWidget->currentRow());
// And remove it
delete item;
}
}
As you can see we iterate all selected items (for set multiple selection mode use setSelectionMode() method) and delete it by ourself, because docs says that
Items removed from a list widget will not be managed by Qt, and will
need to be deleted manually.
Adding some items is easier, my solution with static variable for different item caption looks like:
void MainWindow::addItem()
{
static int i = 0;
listWidget->addItem(QString::number(++i));
}
To simplify your code use Qt5 sytax for signals and slots. It eliminates the need to create intermediate slots.
I hope it helps to you.
It's much simpler than the accepted answer. You don't need to deal with creating a context menu or cursor positions or any of that. Instead of Qt::CustomContextMenu, use Qt::ActionsContextMenu and just add your actions directly to the widget:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui->setupUi(this);
// you can create the actions here, or in designer
auto actInsert = new QAction("Insert", this);
auto actDelete = new QAction("Delete", this);
// you can set up slot connections here or in designer
connect(actInsert, SIGNAL(triggered()), this, SLOT(addItem()));
connect(actDelete, SIGNAL(triggered()), this, SLOT(eraseItem()));
// and this will take care of everything else:
listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
listWidget->addActions({ actInsert, actDelete });
}
void MainWindow::addItem () {
...; // add an item
}
void MainWindow::eraseItem () {
...; // erase an item
}
Everything above except addActions (I think) can also be done in Designer.
Alternatively, if you don't want to add actual slot functions for whatever reason, you can do everything in a lambda on connect, too:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
ui->setupUi(this);
// you can create the actions here, or in designer
auto actInsert = new QAction("Insert", this);
auto actDelete = new QAction("Delete", this);
connect(actInsert, &QAction::triggered, [=]() {
...; // add an item
});
connect(actDelete, &QAction::triggered, [=]() {
...; // erase an item
});
// and this will take care of everything else:
listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
listWidget->addActions({ actInsert, actDelete });
}
The signal/slot option is more organized and flexible, but the lambda option is good for short highly specialized bits of code (or binding to functions that aren't slots).
This works for context menus on any widget. Also, the same QAction can be used on multiple widgets.

UI doesn' t update in qt app

In the code above uitablewidget does not update using signal and slot.
It seems as if (ui->tableWidget->setItem(0,0,newItemx);) doesn't work.
Am I doing something wrong or is there a better way to update my qtablewidget from my class B?
Class_A::Class_A(QWidget *parent):QDialog(parent),ui(new Ui::Class_A)
{
ui->setupUi(this);
}
Class_A::~Class_A()
{
delete ui;
}
void Class_A::change_TableWidget(double x,double y) // this is the public slot
{
QTableWidgetItem *newItemx = new QTableWidgetItem(QString::number(x));
ui->tableWidget->setItem(0,0,newItemx);
QTableWidgetItem *newItemy = new QTableWidgetItem(QString::number(y));
ui->tableWidget->setItem(0,0,newItemy);
}
Class_B::Class_B(QWidget *parent) :
QGLWidget(parent)
{
Class_A *t=new Class_A;
connect(this,SIGNAL(mySignal(double,double)),t,SLOT(change_TableWidget(double,double)));
}
void Class_B::mousePressEvent(QMouseEvent *event)
{
double x = event->x();
double y = event->y();
emit mySignal(x,y);
}
You don't have a SLOT(change_TableWidget(double,double)) - yours takes 3 doubles, not two.
You should check that connect() returned true. I like to write
if (!connect(....)) Q_ASSERT(false);
or if (!connect(....)) Q_ASSERT(!"connect");
Also, connect prints out messages to the debug output when it fails to match the signals and slots. You should look for that output.
(Or use the new Qt 5 connect(), which is all checked at compile time.)