How button onClick works - c++

I am a bit confused with Qt's onClick handling. I have a class which looks like this:
class DatabaseManager : public QObject
{
Q_OBJECT;
private:
QSqlDatabase db;
public slots:
bool openDB(const QString& path);
};
And I have a class which handles the click on the button:
Click::Click(QWidget *parent) : QWidget(parent){
QPushButton *create = new QPushButton("Create database", this);
create->setGeometry(50,100,100,100);
connect(create, SIGNAL(clicked()), this, SLOT(openDB("/home/peter/database.db")));
}
main.cpp
int main(int argc,char **argv){
QApplication *app = new QApplication(argc, argv);
QPushButton btn;
DatabaseManager db;
btn.move(300,300);
btn.resize(250,250);
btn.setWindowTitle("Dibli");
btn.show();
return app->exec();
}
How could I tell to the click handler, that I want to use a specific DatabaseManager object's openDB function? Because it doesn't create the file, if I click on it.
I've updated the code.

assuming your Click class derives from QObject, you should add a slot
public slots:
void onClick() { openDB("/home/peter/database.db"); }
and connect that:
connect(create, SIGNAL(clicked()), this, SLOT(onClick()))
edit Since you show more code now, here is a different hint. Change main like
int main(int argc,char **argv){
QApplication *app = new QApplication(argc, argv);
QPushButton btn;
DatabaseManager db;
db.path = "/home/peter/database.db";
QObject::connect(&btn, SIGNAL(clicked()), &db, SLOT(openDB()));
btn.move(300,300);
btn.resize(250,250);
btn.setWindowTitle("Dibli");
btn.show();
return app->exec();
}
and
class DatabaseManager : public QObject
{
Q_OBJECT;
private:
QSqlDatabase db;
public:
QString path;
public slots:
bool openDB();
};
Note I added a member variable (db.path) to DatabaseManager, and changed the slot openDB removing the argument.
That's because the button' signal cannot provide the string. The easier way then is to make it available in the class.

You cannot call the specific argument instance in the connect function call.
connect is processed by the MOC - meta object compiler - and add some magick to all object that has the macro Q_OBJECT. You have to call a function inside the connect in which you specify only the argument it will receive. (And if they are non qt-object you have to register them with qRegisterMetaType<MyDataType>("MyDataType"); but this is a different story).
So, remember, every time call:
connect(sender, SIGNAL( event() ),
receiver, SLOT( onEvent() ))
and then:
void onEvent() {
mycomplexoperation( ... )
}
EDIT:
thank to Riateche comment, I have to specify that you need qRegisterMetaType<MyDataType>("MyDataType"); with all object not listed in this list. QString not inherits from QObject but could be used in signal/slot system without registration. and thanks to Frank Osterfeld comment I have to add that only for queued signal/slot connections the registration is needed (I didn't know that)
thankd to

Related

It it right way to user a global QObject for cross signal/slot forward

My situation is, a big Qt project with many QWidget communication requirement.
For example, I've a QPushButton B and a QLabel L, I need to click thd button B to show some text on label L or hide it, etc. But the problem is neither of them can get each other's object pointer, because both of them located in a deep QWidget tree, maybe their grandgrandgrand parent widget was sibling, what ever.
So my option, is creating a global unique QObject, just for singal/slot forwad,like this:
class GlobalForward: public QObject
{
Q_OBJECT
public:
GlobalForward():QObject(null) {}
signal:
void SigForwardButton(bool toggle);
}
GlobalForward* gForwardObj;
int main(int argc, char* argv[])
{
QApplication app(argc, argv[]);
gForwardObj = new GlobalForward();
QWidget w;
QPushButton* button = new QPushButton(&w);
QWidget m;
QLabel* label = new QLabel(&m);
w.show();
m.show();
connect(button, &QPushButton::clicked, gForwardObj, &GlobalForward::SigForwardButton);
connect(gForwardObj, &GlobalForward::SigForwardButton, label, &QWidget::hide);
return app.exec();
}
Actually, the button and the label are so faraway that they cannot see each other. I want to use GlobalForward to concat the singnal to make it work . Further more, it can forwad QEvent silmiarly.
What I want to know is, is this way properly solved my problem, and what's the disadvantage of it. Also, any better solution will be appropriate, thanks.

Switching between windows. Qt Widgets ( 1 widget in memory )

For example I have 2 widgets and I press button on first widget. I need to delete first widget and create new widget.
How is it possible? I mean some structure for this. I used stackedwidgets, but pages from stackedwidgets located in memory. I need to avoid this.
void Window::on_registrationButton_clicked(){
ui->logWindow->hide();
ui->RegistrWindow->show();
}
As you are going to eliminate the object you can not do it within the same class the object belongs to, you have to do it outside of it, for example in the following code I created a signal that is triggered when the button is pressed, this I have connected it to a lambda function where the new object is created and the object that emits it is eliminated.
class LogWindow: public QWidget{
Q_OBJECT
public:
LogWindow(const QString &text, QWidget *parent=Q_NULLPTR):QWidget(parent){
setLayout(new QVBoxLayout);
btn = new QPushButton(text, this);
layout()->addWidget(btn);
connect(btn, &QPushButton::clicked, this, &LogWindow::customSignal);
}
signals:
void customSignal();
private:
QPushButton *btn;
};
class RegWindow : public QWidget{
[...]
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
LogWindow *log= new LogWindow("LogWindow");
RegWindow *reg;
QObject::connect(log, &LogWindow::customSignal, [&reg, &log](){
reg = new RegWindow("RegWindow");
reg->show();
log->deleteLater();
});
log->show();
return a.exec();
}
#include "main.moc"
The complete example can be found in the following link

How to add a Menu Bar into a Window Frame? [QT with C++]

I'm a beginner in C++ and I started learning how to use QT components through code at MVS IDE. I still don't know if that was the best option to begin, but since I'm a java programmer, I made the path I made with Java (Swing components). So, my problem is, how to comunicate two class of my code, since in one I made the window frame and in the other I made my menu bar?
In java I would make something like:
JFrame frame = new JFrame();
JMenu menu = new JMenu();
frame.add(menu);
Anyway, This is my code:
#include "Header.h"
class MainWindow{
private:
QWidget *widget;
public:
void buildWindow(QApplication& app){
widget = app.desktop();
QMainWindow *main_window = new QMainWindow();
QWidget *mainWid = new QWidget(main_window);
MyMenuBar myMenuBar(mainWid);
main_window->setWindowState(mainWid->windowState() | Qt::WindowMaximized);
main_window->setWindowTitle("QT Trainning");
main_window->show();
}
};
class MyMenuBar:QMainWindow {
public:
MyMenuBar(QWidget* mainWid){
QAction *quit = new QAction("&Quit", this);
QMenuBar *menu = new QMenuBar(mainWid);
QMenu *file;
menu->addMenu(file);
file = menuBar()->addMenu("&File");
file->addAction(quit);
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow frame;
frame.buildWindow(app);
return app.exec();
}
I tryed to create an Instance of MenuBar inside the Window class but wans't so helpfull and to be honest most of the materials I found to deal with QT interface they supose that you are using the QT GUI...Any tips about how to solve the problem or what should I really do to practice C++??
Thanks in advance
You should specify access specifier for inheritance,otherwise default mode is public.
Also, if you are going to have all the classes in the same file the ordering is important(i think). In your case MyMenuBar should come before MainWindow. So, it is a better practice to have different components in different headers and then include them as necessary.
Here is the code for the case where you need two classes separately:
class TrainingMenu:public QMainWindow {
public:
TrainingMenu(QMenuBar *menubar){
QAction *quit = new QAction("&Quit", menubar);
QMenu *file;
file = menubar->addMenu("&File");
file->addAction(quit);
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
}
};
class MainWindows:public QMainWindow{
private:
TrainingMenu* _menu;
public:
MainWindows(QMainWindow *parent = 0):QMainWindow(parent) {
_menu=new TrainingMenu(MainWindows::menuBar());
this->setWindowTitle("Qt training");
this->setWindowState(Qt::WindowMaximized);
this->show();
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindows window;
return app.exec();
}
This example should be good enough. You do the following:
Create a QMenu with the top widget as a parent
Add submenu QMenu instances to the root level menu

How to get which QradioButton invoke the SLOT

I create several QradioButton and connect to the same SLOT. In the slot, I want to know which QradioButton invoke the slot and do the related action. I found there is a way by using qobject_cast and QObject::sender(), but it seems not work. Here is the code:
header file:
class dialoginput : public QDialog
{
Q_OBJECT
public:
dialoginput(QWidget *parent = 0);
QRadioButton *radio1;
QRadioButton *radio2;
QRadioButton *radio3;
private slots:
void setText_2();
private:
QLabel *label_0_0;
QLabel *label_1;
};
main file:
dialoginput::dialoginput(QWidget *parent): QDialog(parent){
label_0_0 = new QLabel("label_1:");
label_1 = new QLabel;
QWidget *window = new QWidget;
QVBoxLayout *windowLayout = new QVBoxLayout;
QGroupBox *box = new QGroupBox("Display Type");
radio1 = new QRadioButton("3");
radio2 = new QRadioButton("5");
radio3 = new QRadioButton("9");
QVBoxLayout *radioLayout = new QVBoxLayout;
connect(radio1,SIGNAL(clicked()),this,SLOT(setText_2()));
connect(radio2,SIGNAL(clicked()),this,SLOT(setText_2()));
connect(radio3,SIGNAL(clicked()),this,SLOT(setText_2()));
radioLayout->addWidget(radio1);
radioLayout->addWidget(radio2);
radioLayout->addWidget(radio3);
box->setLayout(radioLayout);
windowLayout->addWidget(box);
windowLayout->addWidget(label_0_0);
windowLayout->addWidget(label_1);
window->setLayout(windowLayout);
window->show();
}
void dialoginput::setText_2(){
QObject *object = QObject::sender();
QRadioButton* pbtn = qobject_cast<QRadioButton*>(object);
QString name = pbtn->objectName();
label_1->setText(name);
if(!QString::compare(name, "3")){
}
else if(!QString::compare(name, "5")){
}
else if(!QString::compare(name, "9")){
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
dialoginput *input = new dialoginput();
return a.exec();
}
Even though using the sender() method solves your problem, i do not recommend using it. The problem is, signals and slots are designed to seperate the emitter and the receiver. A receiver does not need to know which objects, even what types of objects can trigger its slot. When you use sender(), you are relying on the fact that the receiver has knowledge of all of the objects that triggers its slot. What if this changes in the future?
You should take a look at QSignalMapper, it is designed specifically for this kind of needs. There are good examples about it in the docs.
You could create separate wrapper slots for each radio button, which then passes the information to the function you want to call. Something like this: -
class dialoginput : public QDialog
{
Q_OBJECT
public:
QRadioButton *radio1;
QRadioButton *radio2;
QRadioButton *radio3;
private slots:
void Radio1Selected() { setText_2(1); }
void Radio2Selected() { setText_2(2); }
void Radio3Selected() { setText_2(3); }
private:
void setText_2(int id);
};
Then connect each radio button: -
connect(radio1,SIGNAL(clicked()),this,SLOT(Radio1Selected()));
connect(radio2,SIGNAL(clicked()),this,SLOT(Radio2Selected()));
connect(radio3,SIGNAL(clicked()),this,SLOT(Radio3Selected()));
Now when setText_2 is called, the id will represent the selected radio button.
You are getting sender Object correctly on setText_2(), But you are not setting objectName property of radio1, radio2 and radio3. Please use "setObjectName( )" API.
write single argument custom signal for radiobuttons and then emit it .catch that argument in slot.check for corresponding radio button
You could also create a QButtonGroup and use lambda expression (c++11)
class dialoginput : public QDialog
{
Q_OBJECT
public:
private:
void setText_2(int id);
QRadioButton *radio1;
QRadioButton *radio2;
QRadioButton *radio3;
QButtonGroup _btnGroup;
};
After add the 3 QRadioButton to the QButtonGroup
_btnGroup.addButton(radio1, 1);
_btnGroup.addButton(radio2, 2);
_btnGroup.addButton(radio3, 3);
connect(&_btnGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), [=](int id){
setText_2(id);});

In Qt how do I get a button press to set a spinbox to a certain value?

I'm trying to get to grips with Qt's signal and slots mechanism. I have an app with a QPushButton and a QSpinBox. When I click the button I want the spinbox to change to 20. What signal and slot do I need to set up?
The code below shows the app, the connect function is the one I am having trouble with.
As I understand it the setValue(int) slot of QSpinBox will not work here because the clicked() signal of QPushButton has a different signature, and anyway how would I pass the value 20 to the spinbox? Do I need to write some sort of auxiliary function to act as a slot which calls spinbox->setValue(20)? If so, what form would that take?
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = new QWidget;
QSpinBox *spinbox = new QSpinBox;
QPushButton *button = new QPushButton("Set to 20");
spinbox->setRange(0, 100);
// What should I put below?
QObject::connect(button, SIGNAL(clicked()), spinbox, SLOT(???????));
QLayout *layout = new QHBoxLayout;
layout->addWidget(spinbox);
layout->addWidget(button);
window->setLayout(layout);
window->show();
return app.exec();
}
You can either do:
class AuxSignals : public QObject
{
Q_OBJECT
...
signals:
void valueChanged(int);
public slots:
void buttonClicked() { emit valueChanged(20); }
};
...
// On main.cpp
AuxSignals *auxSignals = new AuxSignals;
QObject::connect(button, SIGNAL(clicked()), auxSignal, SLOT(buttonClicked));
QObject::connect(auxSignals, SIGNAL(valueChanged(int)), spinbox, SLOT(setValue(int)));
or
class AuxSignals : public QObject
{
Q_OBJECT
...
QSpinBox *m_spinBox;
public:
AuxSignals(QSpinBox *spinBox) : m_spinBox(spinBox) {}
public slots:
void buttonClicked() { m_spinBox->setValue(20); }
};
...
// On main.cpp
AuxSignals *auxSignals = new AuxSignals(spinBox);
QObject::connect(button, SIGNAL(clicked()), auxSignals, SLOT(buttonClicked()));
I prefer the first option because it doesn't require the AuxSignals class to have a pointer to a specific QWidget.
I think you are looking at a custom SLOT here. A QPushButton::clicked signal will give a boolean (true|false) event. If you catch it using QSpinBox::setValue you won't go very far. The QSpinBox::setValue expects an int and converts the input boolean to 0 or 1 as the case maybe and your spinbox increments by only 1 unit. If you were to write a custom SLOT you can actually set the exact slider value with far more control.